From 7a41dcb620f67b047d9ae167e64a593b1dcb4601 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 01:18:04 -0700 Subject: [PATCH 1/6] updated goprocess (SetTeardown fix) License: MIT Signed-off-by: Juan Batiz-Benet --- Godeps/Godeps.json | 2 +- .../github.com/jbenet/goprocess/goprocess.go | 2 -- .../jbenet/goprocess/goprocess_test.go | 2 -- .../github.com/jbenet/goprocess/impl-mutex.go | 28 +++++++++---------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1797c4f887d..d1663a33601 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -204,7 +204,7 @@ }, { "ImportPath": "github.com/jbenet/goprocess", - "Rev": "788dcf5ca3517f243d276394545ca6b3b4ac32d5" + "Rev": "4562d0c5780b8f060df2b84a8945bb8678bfc023" }, { "ImportPath": "github.com/kardianos/osext", diff --git a/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go b/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go index 17cb37799a1..b027d82144d 100644 --- a/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go +++ b/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go @@ -145,8 +145,6 @@ type Process interface { // lifecycle of a Process. type TeardownFunc func() error -var nilTeardownFunc = func() error { return nil } - // ProcessFunc is a function that takes a process. Its main use case is goprocess.Go, // which spawns a ProcessFunc in its own goroutine, and returns a corresponding // Process object. diff --git a/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go b/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go index 81c19584351..3a16de7e63f 100644 --- a/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go @@ -300,13 +300,11 @@ func TestAddChild(t *testing.T) { testNone(t, Q) go b.Close() - testNone(t, Q) d.Close() testStrs(t, Q, "b", "d") testStrs(t, Q, "b", "d") go a.Close() - testNone(t, Q) c.Close() testStrs(t, Q, "a", "c") testStrs(t, Q, "a", "c") diff --git a/Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go b/Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go index c6acc4f419b..a67deea75ad 100644 --- a/Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go +++ b/Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go @@ -25,10 +25,6 @@ type process struct { // **after** entering <-Closing(), and // **before** <-Closed(). func newProcess(tf TeardownFunc) *process { - if tf == nil { - tf = nilTeardownFunc - } - return &process{ teardown: tf, closed: make(chan struct{}), @@ -123,17 +119,19 @@ func (p *process) Go(f ProcessFunc) Process { // SetTeardown to assign a teardown function func (p *process) SetTeardown(tf TeardownFunc) { if tf == nil { - tf = nilTeardownFunc + panic("cannot set nil TeardownFunc") } p.Lock() - if p.teardown == nil { - select { - case <-p.Closed(): - p.teardown = tf - p.closeErr = tf() - default: - } + if p.teardown != nil { + panic("cannot SetTeardown twice") + } + + p.teardown = tf + select { + case <-p.Closed(): + p.closeErr = tf() + default: } p.Unlock() } @@ -196,8 +194,10 @@ func (p *process) doClose() { } } - p.closeErr = p.teardown() // actually run the close logic (ok safe to teardown) - close(p.closed) // signal that we're shut down (Closed) + if p.teardown != nil { + p.closeErr = p.teardown() // actually run the close logic (ok safe to teardown) + } + close(p.closed) // signal that we're shut down (Closed) // go remove all the parents from the process links. optimization. go func(waiters []*processLink) { From 578fd02ce374c7a0604f7d21a1ba5d25e561a762 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 01:36:50 -0700 Subject: [PATCH 2/6] fuse unmount fixes unmounting wasn't happening, mostly because of a recent bug in goprocess.SetTeardown. This commit bumps up some messages to log.Warnings, as users may want to see them, and makes sure to Unmount when a node shuts down. License: MIT Signed-off-by: Juan Batiz-Benet --- core/commands/mount_unix.go | 2 +- core/core.go | 7 +++++++ fuse/mount/fuse.go | 4 ++-- fuse/mount/mount.go | 18 ++++++++++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/core/commands/mount_unix.go b/core/commands/mount_unix.go index c3410a6f965..d86ea393551 100644 --- a/core/commands/mount_unix.go +++ b/core/commands/mount_unix.go @@ -216,7 +216,7 @@ func doMount(node *core.IpfsNode, fsdir, nsdir string) error { <-done if err1 != nil || err2 != nil { - log.Infof("error mounting: %s %s", err1, err2) + log.Errorf("error mounting: %s %s", err1, err2) if fsmount != nil { fsmount.Unmount() } diff --git a/core/core.go b/core/core.go index 75e852354bd..d8412f38d59 100644 --- a/core/core.go +++ b/core/core.go @@ -382,6 +382,13 @@ func (n *IpfsNode) teardown() error { n.Repo, } + if n.Mounts.Ipfs != nil { + closers = append(closers, mount.Closer(n.Mounts.Ipfs)) + } + if n.Mounts.Ipns != nil { + closers = append(closers, mount.Closer(n.Mounts.Ipns)) + } + // Filesystem needs to be closed before network, dht, and blockservice // so it can use them as its shutting down if n.IpnsFs != nil { diff --git a/fuse/mount/fuse.go b/fuse/mount/fuse.go index c00dcebc879..6e556978c15 100644 --- a/fuse/mount/fuse.go +++ b/fuse/mount/fuse.go @@ -96,7 +96,7 @@ func (m *mount) unmount() error { if err == nil { return nil } - log.Debug("fuse unmount err: %s", err) + log.Warningf("fuse unmount err: %s", err) // try closing the fuseConn err = m.fuseConn.Close() @@ -104,7 +104,7 @@ func (m *mount) unmount() error { return nil } if err != nil { - log.Debug("fuse conn error: %s", err) + log.Warningf("fuse conn error: %s", err) } // try mount.ForceUnmountManyTimes diff --git a/fuse/mount/mount.go b/fuse/mount/mount.go index 0adc05f5551..5ec8d597f18 100644 --- a/fuse/mount/mount.go +++ b/fuse/mount/mount.go @@ -3,6 +3,7 @@ package mount import ( "fmt" + "io" "os/exec" "runtime" "time" @@ -33,7 +34,7 @@ type Mount interface { // It does so by calling diskutil or fusermount directly. func ForceUnmount(m Mount) error { point := m.MountPoint() - log.Infof("Force-Unmounting %s...", point) + log.Warningf("Force-Unmounting %s...", point) var cmd *exec.Cmd switch runtime.GOOS { @@ -59,7 +60,7 @@ func ForceUnmount(m Mount) error { }() select { - case <-time.After(2 * time.Second): + case <-time.After(7 * time.Second): return fmt.Errorf("umount timeout") case err := <-errc: return err @@ -81,3 +82,16 @@ func ForceUnmountManyTimes(m Mount, attempts int) error { } return fmt.Errorf("Unmount %s failed after 10 seconds of trying.", m.MountPoint()) } + +type closer struct { + M Mount +} + +func (c *closer) Close() error { + log.Error(" (c *closer) Close(),", c.M.MountPoint()) + return c.M.Unmount() +} + +func Closer(m Mount) io.Closer { + return &closer{m} +} From 6e6badf105d446f03afe6fab4bc7bba64ef9c520 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 01:39:32 -0700 Subject: [PATCH 3/6] add -w: fix to work correctly with dirs. this commit changes the behavior of ipfs add -w: - it makes it able to work with ipfs add -r - instead of hacking around the add, we simply just add a wrapper directory around the whole result of the add. this means that ipfs add -w calls will output _two_ lines, but this is actually more correct than outputting one line, as two objects were added. this _may_ break scripts out there which expect the output to look a certain way. we should consider whether the old output is more _useful_ (even if less in-line with the model.) License: MIT Signed-off-by: Juan Batiz-Benet --- core/commands/add.go | 20 +++++-------------- test/sharness/t0040-add-and-cat.sh | 32 ++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/core/commands/add.go b/core/commands/add.go index 6b96d8f9035..5f2dc20ba36 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -10,7 +10,6 @@ import ( cmds "github.com/ipfs/go-ipfs/commands" files "github.com/ipfs/go-ipfs/commands/files" core "github.com/ipfs/go-ipfs/core" - coreunix "github.com/ipfs/go-ipfs/core/coreunix" importer "github.com/ipfs/go-ipfs/importer" "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" @@ -128,10 +127,14 @@ remains to be implemented. node: n, out: outChan, progress: progress, - wrap: wrap, hidden: hidden, trickle: trickle, } + + if wrap { + file = files.NewSliceFile("", []files.File{file}) + } + rootnd, err := addParams.addFile(file) if err != nil { res.SetError(err, cmds.ErrNormal) @@ -247,7 +250,6 @@ type adder struct { node *core.IpfsNode out chan interface{} progress bool - wrap bool hidden bool trickle bool } @@ -299,18 +301,6 @@ func (params *adder) addFile(file files.File) (*dag.Node, error) { reader = &progressReader{file: file, out: params.out} } - if params.wrap { - p, dagnode, err := coreunix.AddWrapped(params.node, reader, path.Base(file.FileName())) - if err != nil { - return nil, err - } - params.out <- &AddedObject{ - Hash: p, - Name: file.FileName(), - } - return dagnode, nil - } - dagnode, err := add(params.node, reader, params.trickle) if err != nil { return nil, err diff --git a/test/sharness/t0040-add-and-cat.sh b/test/sharness/t0040-add-and-cat.sh index 0e3e6fb08fd..148548616fc 100755 --- a/test/sharness/t0040-add-and-cat.sh +++ b/test/sharness/t0040-add-and-cat.sh @@ -212,6 +212,28 @@ test_expect_success "'ipfs cat' output looks good" ' test_cmp mountdir/bigfile actual ' +test_expect_success "ipfs add -w succeeds" ' + ipfs add -w mountdir/hello.txt >actual +' + +test_expect_success "ipfs add -w output looks good" ' + HASH_W1="QmVr26fY1tKyspEJBniVhqxQeEjhF78XerGiqWAwraVLQH" && + HASH_W2="QmVJfrqd4ogGZME6LWkkikAGddYgh9dBs2U14DHZZUBk7W" && + echo "added $HASH_W1 mountdir/hello.txt" >expected && + echo "added $HASH_W2 " >>expected && + test_cmp expected actual +' + +test_expect_success "ipfs add -w succeeds (dir)" ' + ipfs add -r -w mountdir | tail -n1 >actual +' + +test_expect_success "ipfs add -w output looks good (dir)" ' + HASH_W="Qmc341yGztU1o8n3c1u5xTYF3uE3zPPP2NYemG9MKz775V" && + echo "added $HASH_W " >expected && + test_cmp expected actual +' + test_expect_success FUSE "cat ipfs/bigfile succeeds" ' cat "ipfs/$HASH" >actual ' @@ -262,16 +284,6 @@ test_expect_success FUSE,EXPENSIVE "cat ipfs/bigfile looks good" ' test_cmp sha1_expected sha1_actual ' -test_expect_success "ipfs add -w succeeds" ' - ipfs add -w mountdir/hello.txt >actual -' - -test_expect_success "ipfs add -w output looks good" ' - HASH="QmVJfrqd4ogGZME6LWkkikAGddYgh9dBs2U14DHZZUBk7W" && - echo "added $HASH/hello.txt mountdir/hello.txt" >expected && - test_cmp expected actual -' - test_kill_ipfs_daemon test_done From 4a7c1cf802d069f59e25f0d057d0b19997449612 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 03:07:39 -0700 Subject: [PATCH 4/6] add -w improvement: wrap multiple files in one dir > ipfs add a b c added Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 a added QmR9pC5uCF3UExca8RSrCVL8eKv7nHMpATzbEQkAHpXmVM b added QmetGxZTgo8tYAKQH1KLsY13MxqeVHbxYVmvzBzJAKU6Z7 c added QmXg3WHLcjnz4ejeYF6FKVBkb4m1oKjQmF5fEWL9M1uQF3 > ipfs ls QmXg3WHLcjnz4ejeYF6FKVBkb4m1oKjQmF5fEWL9M1uQF3 Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 10 a QmR9pC5uCF3UExca8RSrCVL8eKv7nHMpATzbEQkAHpXmVM 10 b QmetGxZTgo8tYAKQH1KLsY13MxqeVHbxYVmvzBzJAKU6Z7 10 c License: MIT Signed-off-by: Juan Batiz-Benet --- commands/files/multipartfile.go | 4 ++ core/commands/add.go | 87 +++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/commands/files/multipartfile.go b/commands/files/multipartfile.go index 844e0afa9c8..ff2aab56b4a 100644 --- a/commands/files/multipartfile.go +++ b/commands/files/multipartfile.go @@ -68,6 +68,10 @@ func (f *MultipartFile) NextFile() (File, error) { } func (f *MultipartFile) FileName() string { + if f == nil || f.Part == nil { + return "" + } + filename, err := url.QueryUnescape(f.Part.FileName()) if err != nil { // if there is a unescape error, just treat the name as unescaped diff --git a/core/commands/add.go b/core/commands/add.go index 5f2dc20ba36..de5cc4d850b 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -110,52 +110,65 @@ remains to be implemented. outChan := make(chan interface{}, 8) res.SetOutput((<-chan interface{})(outChan)) - go func() { - defer close(outChan) + // addSingleFile is a function that adds a file given as a param. + addSingleFile := func(file files.File) error { + addParams := adder{ + node: n, + out: outChan, + progress: progress, + hidden: hidden, + trickle: trickle, + } - for { - file, err := req.Files().NextFile() - if err != nil && err != io.EOF { - res.SetError(err, cmds.ErrNormal) - return - } - if file == nil { // done - return - } + rootnd, err := addParams.addFile(file) + if err != nil { + return err + } - addParams := adder{ - node: n, - out: outChan, - progress: progress, - hidden: hidden, - trickle: trickle, - } + rnk, err := rootnd.Key() + if err != nil { + return err + } - if wrap { - file = files.NewSliceFile("", []files.File{file}) - } + mp := n.Pinning.GetManual() + mp.RemovePinWithMode(rnk, pin.Indirect) + mp.PinWithMode(rnk, pin.Recursive) + return n.Pinning.Flush() + } - rootnd, err := addParams.addFile(file) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return + // addFilesSeparately loops over a convenience slice file to + // add each file individually. e.g. 'ipfs add a b c' + addFilesSeparately := func(sliceFile files.File) error { + for { + file, err := sliceFile.NextFile() + if err != nil && err != io.EOF { + return err + } + if file == nil { + return nil // done } - rnk, err := rootnd.Key() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return + if err := addSingleFile(file); err != nil { + return err } + } + } - mp := n.Pinning.GetManual() - mp.RemovePinWithMode(rnk, pin.Indirect) - mp.PinWithMode(rnk, pin.Recursive) + go func() { + defer close(outChan) - err = n.Pinning.Flush() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } + // really, we're unrapping, if !wrap, because + // req.Files() is already a SliceFile() with all of them, + // so can just use that slice as the wrapper. + var err error + if wrap { + err = addSingleFile(req.Files()) + } else { + err = addFilesSeparately(req.Files()) + } + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } }() }, From 431d6a5a1407bfbcfb28a798657fa90e68a833a5 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 03:18:24 -0700 Subject: [PATCH 5/6] added random-files tool for testing License: MIT Signed-off-by: Juan Batiz-Benet --- Godeps/Godeps.json | 4 + .../github.com/jbenet/go-random-files/LICENSE | 21 ++++ .../jbenet/go-random-files/Makefile | 5 + .../jbenet/go-random-files/README.md | 108 ++++++++++++++++ .../github.com/jbenet/go-random-files/lib.go | 110 ++++++++++++++++ .../go-random-files/random-files/.gitignore | 1 + .../go-random-files/random-files/README.md | 90 +++++++++++++ .../go-random-files/random-files/main.go | 119 ++++++++++++++++++ .../go-random-files/ringreader/ringreader.go | 40 ++++++ .../ringreader/ringreader_test.go | 22 ++++ test/Makefile | 5 + test/sharness/Makefile | 2 +- test/sharness/lib/random-dep.go | 1 + 13 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/LICENSE create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/Makefile create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/README.md create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/lib.go create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/.gitignore create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/README.md create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/main.go create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader.go create mode 100644 Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d1663a33601..4c86e55f8bd 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -186,6 +186,10 @@ "ImportPath": "github.com/jbenet/go-random", "Rev": "cd535bd25356746b9b1e824871dda7da932460e2" }, + { + "ImportPath": "github.com/jbenet/go-random-files", + "Rev": "737479700b40b4b50e914e963ce8d9d44603e3c8" + }, { "ImportPath": "github.com/jbenet/go-reuseport", "Rev": "48959f1fad204b6cf2c0e8d086ef69f03f2de961" diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/LICENSE b/Godeps/_workspace/src/github.com/jbenet/go-random-files/LICENSE new file mode 100644 index 00000000000..c7386b3c940 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Juan Batiz-Benet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/Makefile b/Godeps/_workspace/src/github.com/jbenet/go-random-files/Makefile new file mode 100644 index 00000000000..5a72abe1fa3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/Makefile @@ -0,0 +1,5 @@ +build: + cd random-files && go build + +install: + cd random-files && go install diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/README.md b/Godeps/_workspace/src/github.com/jbenet/go-random-files/README.md new file mode 100644 index 00000000000..f2fb4b9b47e --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/README.md @@ -0,0 +1,108 @@ +# go-random-files - create random fs hierarchies + +Useful for testing filesystems. + + +``` +# library +go get -u github.com/jbenet/go-random-files + +# binary +go get -u github.com/jbenet/go-random-files/random-files +``` + +### Library godoc: https://godoc.org/github.com/jbenet/go-random-files + +# random-files - create random fs hierarchies + +See more about the binary at: github.com/jbenet/go-random-files/tree/master/random-files + +Useful for testing filesystems. + +## Install + +``` +go get -u github.com/jbenet/go-random-files/random-files +``` + +## Usage + +```sh +> random-files --help +usage: random-files [options] ... +Write a random filesystem hierarchy to each + +Options: + -alphabet="easy": alphabet for filenames {easy, hard} + -depth=2: fanout depth - how deep the hierarchy goes + -dirs=5: fanout dirs - number of dirs per dir (or max) + -files=10: fanout files - number of files per dir (or max + -filesize=4096: filesize - how big to make each file (or max) + -q=false: quiet output + -random-crypto=false: use cryptographic randomness for files + -random-fanout=false: randomize fanout numbers + -random-size=true: randomize filesize + -seed=0: random seed - 0 for current time +``` + +### Examples + +```sh +> random-files --depth=2 --files=3 foo +foo/h20uo3jrpihb +foo/x6tef1 +foo/jh0c2vdci +foo/fden012m368 +foo/fden012m368/p6n0chy4kg +foo/fden012m368/h92_ +foo/fden012m368/kvjiya98p3 +foo/e_i6hwav1tb +foo/e_i6hwav1tb/oj0-a +foo/e_i6hwav1tb/1-pfgvim +foo/e_i6hwav1tb/s_unf +foo/bgvy8x-_hsm +foo/bgvy8x-_hsm/98zcoz-9ng +foo/bgvy8x-_hsm/j0see3qv +foo/bgvy8x-_hsm/qntuf0r +foo/6zjkw3ejm2awwt +foo/6zjkw3ejm2awwt/iba52dh1lhnewh +foo/6zjkw3ejm2awwt/n1bwcv5zpe +foo/6zjkw3ejm2awwt/o8k89cc +foo/efp_6 +foo/efp_6/qfap2 +foo/efp_6/v_kl_wlefsaa +foo/efp_6/r7sdbph +``` + +It made: + +``` +> tree foo +foo +├── 6zjkw3ejm2awwt +│   ├── iba52dh1lhnewh +│   ├── n1bwcv5zpe +│   └── o8k89cc +├── bgvy8x-_hsm +│   ├── 98zcoz-9ng +│   ├── j0see3qv +│   └── qntuf0r +├── e_i6hwav1tb +│   ├── 1-pfgvim +│   ├── oj0-a +│   └── s_unf +├── efp_6 +│   ├── qfap2 +│   ├── r7sdbph +│   └── v_kl_wlefsaa +├── fden012m368 +│   ├── h92_ +│   ├── kvjiya98p3 +│   └── p6n0chy4kg +├── h20uo3jrpihb +├── jh0c2vdci +└── x6tef1 + +5 directories, 18 files +``` + diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/lib.go b/Godeps/_workspace/src/github.com/jbenet/go-random-files/lib.go new file mode 100644 index 00000000000..e49ad68e235 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/lib.go @@ -0,0 +1,110 @@ +package randomfiles + +import ( + "fmt" + "io" + "math/rand" + "os" + "path" +) + +type Options struct { + Out io.Writer // output progress + Source io.Reader // randomness source + + FileSize int // the size per file. + Alphabet []rune // for filenames + + FanoutDepth int // how deep the hierarchy goes + FanoutFiles int // how many files per dir + FanoutDirs int // how many dirs per dir + + RandomSeed int64 // use a random seed. if 0, use a random seed + RandomSize bool // randomize file sizes + RandomFanout bool // randomize fanout numbers +} + +func WriteRandomFiles(root string, depth int, opts *Options) error { + + numfiles := opts.FanoutFiles + if opts.RandomFanout { + numfiles = rand.Intn(numfiles) + 1 + } + + for i := 0; i < numfiles; i++ { + if err := WriteRandomFile(root, opts); err != nil { + return err + } + } + + if depth+1 <= opts.FanoutDepth { + numdirs := opts.FanoutDirs + if opts.RandomFanout { + numdirs = rand.Intn(numdirs) + 1 + } + + for i := 0; i < numdirs; i++ { + if err := WriteRandomDir(root, depth+1, opts); err != nil { + return err + } + } + } + + return nil +} + +var FilenameSize = 16 +var RunesEasy = []rune("abcdefghijklmnopqrstuvwxyz01234567890-_") +var RunesHard = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%^&*()-_+= ;.,<>'\"[]{}() ") + +func RandomFilename(length int, alphabet []rune) string { + b := make([]rune, length) + for i := range b { + b[i] = alphabet[rand.Intn(len(alphabet))] + } + return string(b) +} + +func WriteRandomFile(root string, opts *Options) error { + filesize := int64(opts.FileSize) + if opts.RandomSize { + filesize = rand.Int63n(filesize) + 1 + } + + n := rand.Intn(FilenameSize-4) + 4 + name := RandomFilename(n, opts.Alphabet) + filepath := path.Join(root, name) + f, err := os.Create(filepath) + if err != nil { + return err + } + + if _, err := io.CopyN(f, opts.Source, filesize); err != nil { + return err + } + + if opts.Out != nil { + fmt.Fprintln(opts.Out, filepath) + } + + return f.Close() +} + +func WriteRandomDir(root string, depth int, opts *Options) error { + if depth > opts.FanoutDepth { + return nil + } + + n := rand.Intn(FilenameSize-4) + 4 + name := RandomFilename(n, opts.Alphabet) + root = path.Join(root, name) + if err := os.MkdirAll(root, 0755); err != nil { + return err + } + + if opts.Out != nil { + fmt.Fprintln(opts.Out, root) + } + + return WriteRandomFiles(root, depth, opts) +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/.gitignore b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/.gitignore new file mode 100644 index 00000000000..03d7e701f05 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/.gitignore @@ -0,0 +1 @@ +random-files diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/README.md b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/README.md new file mode 100644 index 00000000000..acb230e7628 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/README.md @@ -0,0 +1,90 @@ +# random-files - create random fs hierarchies + +random-files creates random fs hierarchies. Useful for testing filesystems. + +## Install + +``` +go get -u github.com/jbenet/go-random-files/random-files +``` + +## Usage + +```sh +> random-files --help +usage: random-files [options] ... +Write a random filesystem hierarchy to each + +Options: + -alphabet="easy": alphabet for filenames {easy, hard} + -depth=2: fanout depth - how deep the hierarchy goes + -dirs=5: fanout dirs - number of dirs per dir (or max) + -files=10: fanout files - number of files per dir (or max + -filesize=4096: filesize - how big to make each file (or max) + -q=false: quiet output + -random-crypto=false: use cryptographic randomness for files + -random-fanout=false: randomize fanout numbers + -random-size=true: randomize filesize + -seed=0: random seed - 0 for current time +``` + +## Examples + +```sh +> random-files --depth=2 --files=3 foo +foo/h20uo3jrpihb +foo/x6tef1 +foo/jh0c2vdci +foo/fden012m368 +foo/fden012m368/p6n0chy4kg +foo/fden012m368/h92_ +foo/fden012m368/kvjiya98p3 +foo/e_i6hwav1tb +foo/e_i6hwav1tb/oj0-a +foo/e_i6hwav1tb/1-pfgvim +foo/e_i6hwav1tb/s_unf +foo/bgvy8x-_hsm +foo/bgvy8x-_hsm/98zcoz-9ng +foo/bgvy8x-_hsm/j0see3qv +foo/bgvy8x-_hsm/qntuf0r +foo/6zjkw3ejm2awwt +foo/6zjkw3ejm2awwt/iba52dh1lhnewh +foo/6zjkw3ejm2awwt/n1bwcv5zpe +foo/6zjkw3ejm2awwt/o8k89cc +foo/efp_6 +foo/efp_6/qfap2 +foo/efp_6/v_kl_wlefsaa +foo/efp_6/r7sdbph +``` + +It made: + +``` +> tree foo +foo +├── 6zjkw3ejm2awwt +│   ├── iba52dh1lhnewh +│   ├── n1bwcv5zpe +│   └── o8k89cc +├── bgvy8x-_hsm +│   ├── 98zcoz-9ng +│   ├── j0see3qv +│   └── qntuf0r +├── e_i6hwav1tb +│   ├── 1-pfgvim +│   ├── oj0-a +│   └── s_unf +├── efp_6 +│   ├── qfap2 +│   ├── r7sdbph +│   └── v_kl_wlefsaa +├── fden012m368 +│   ├── h92_ +│   ├── kvjiya98p3 +│   └── p6n0chy4kg +├── h20uo3jrpihb +├── jh0c2vdci +└── x6tef1 + +5 directories, 18 files +``` diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/main.go b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/main.go new file mode 100644 index 00000000000..b466916a1e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/random-files/main.go @@ -0,0 +1,119 @@ +package main + +import ( + crand "crypto/rand" + "errors" + "flag" + "fmt" + "math/rand" + "os" + "time" + + randomfiles "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random-files" + ringreader "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader" +) + +var usage = `usage: %s [options] ... +Write a random filesystem hierarchy to each + +Options: +` + +// flags +var opts randomfiles.Options +var quiet bool +var alphabet string +var paths []string +var cryptorand bool + +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, usage, os.Args[0]) + flag.PrintDefaults() + } + + flag.BoolVar(&quiet, "q", false, "quiet output") + flag.BoolVar(&cryptorand, "random-crypto", false, "use cryptographic randomness for files") + flag.StringVar(&alphabet, "alphabet", "easy", "alphabet for filenames {easy, hard}") + flag.IntVar(&opts.FileSize, "filesize", 4096, "filesize - how big to make each file (or max)") + + flag.IntVar(&opts.FanoutDepth, "depth", 2, "fanout depth - how deep the hierarchy goes") + flag.IntVar(&opts.FanoutDirs, "dirs", 5, "fanout dirs - number of dirs per dir (or max)") + flag.IntVar(&opts.FanoutFiles, "files", 10, "fanout files - number of files per dir (or max") + + flag.Int64Var(&opts.RandomSeed, "seed", 0, "random seed - 0 for current time") + flag.BoolVar(&opts.RandomFanout, "random-fanout", false, "randomize fanout numbers") + flag.BoolVar(&opts.RandomSize, "random-size", true, "randomize filesize") +} + +func parseArgs() error { + flag.Parse() + + switch alphabet { + case "easy": + opts.Alphabet = randomfiles.RunesEasy + case "hard": + opts.Alphabet = randomfiles.RunesHard + default: + return errors.New("alphabet must be one of: easy, hard") + } + + paths = flag.Args() + if len(paths) < 1 { + flag.Usage() + os.Exit(0) + } + + if !quiet { + opts.Out = os.Stdout + } + + switch opts.RandomSeed { + case 0: + rand.Seed(time.Now().UnixNano()) + default: + rand.Seed(opts.RandomSeed) + } + + // prepare randomn source. + if cryptorand { + opts.Source = crand.Reader + } else { + // if not crypto, we don't need a lot of random + // data. we just need to sample from a sequence. + s := 16777216 // 16MB + r, err := ringreader.NewReader(s) + if err != nil { + return err + } + opts.Source = r + } + + return nil +} + +func run() error { + if err := parseArgs(); err != nil { + return err + } + + for _, root := range paths { + if err := os.MkdirAll(root, 0755); err != nil { + return err + } + + err := randomfiles.WriteRandomFiles(root, 1, &opts) + if err != nil { + return err + } + } + + return nil +} + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader.go b/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader.go new file mode 100644 index 00000000000..c40dbb4249c --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader.go @@ -0,0 +1,40 @@ +package ringreader + +import ( + "bytes" + "fmt" + "math/rand" + + random "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random" +) + +type Reader struct { + Buf []byte +} + +func NewReader(bufsize int) (*Reader, error) { + buf := bytes.NewBuffer(nil) + err := random.WritePseudoRandomBytes(int64(bufsize), buf, rand.Int63()) + return &Reader{Buf: buf.Bytes()}, err +} + +func (r *Reader) Read(buf []byte) (n int, err error) { + ibufl := len(r.Buf) + left := len(buf) + copied := 0 + + for copied < left { + pos1 := rand.Intn(len(r.Buf)) + pos2 := pos1 + left + if pos2 > ibufl { + pos2 = ibufl + } + copied += copy(buf[copied:], r.Buf[pos1:pos2]) + } + + if copied != left { + err := fmt.Errorf("copied a different ammount: %d != %d", copied, left) + panic(err.Error()) + } + return copied, nil +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader_test.go b/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader_test.go new file mode 100644 index 00000000000..791aa070ecb --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-random-files/ringreader/ringreader_test.go @@ -0,0 +1,22 @@ +package ringreader + +import ( + "testing" +) + +func TestRingReader(t *testing.T) { + r, _ := NewReader(256) + t.Log("buffer:", r.Buf) + + for i := 1; i < 1048576; i = i * 2 { + buf := make([]byte, i) + n, err := r.Read(buf) + if err != nil { + t.Error(err) + } + if n != len(buf) { + t.Error("did not read %d bytes", n) + } + t.Log("read:", buf) + } +} diff --git a/test/Makefile b/test/Makefile index cc5e1e933bb..ccc33e181fe 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,6 +3,7 @@ BINS = bin/random bin/multihash bin/ipfs bin/pollEndpoint bin/iptb bin/go-sleep IPFS_ROOT = ../ IPFS_CMD = ../cmd/ipfs RANDOM_SRC = ../Godeps/_workspace/src/github.com/jbenet/go-random +RANDOM_FILES_SRC = ../Godeps/_workspace/src/github.com/jbenet/go-random-files MULTIHASH_SRC = ../Godeps/_workspace/src/github.com/jbenet/go-multihash IPTB_SRC = ../Godeps/_workspace/src/github.com/whyrusleeping/iptb POLLENDPOINT_SRC= ../thirdparty/pollEndpoint @@ -26,6 +27,10 @@ bin/random: $(call find_go_files, $(RANDOM_SRC)) IPFS-BUILD-OPTIONS @echo "*** installing $@ ***" go build $(GOFLAGS) -o bin/random $(RANDOM_SRC)/random +bin/random-files: + @echo "*** installing $@ ***" + go build $(GOFLAGS) -o bin/random-files $(RANDOM_FILES_SRC)/random-files + bin/multihash: $(call find_go_files, $(MULTIHASH_SRC)) IPFS-BUILD-OPTIONS @echo "*** installing $@ ***" go build $(GOFLAGS) -o bin/multihash $(MULTIHASH_SRC)/multihash diff --git a/test/sharness/Makefile b/test/sharness/Makefile index fd3869428c9..20431db5479 100644 --- a/test/sharness/Makefile +++ b/test/sharness/Makefile @@ -8,7 +8,7 @@ T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) BINS = bin/random bin/multihash bin/ipfs bin/pollEndpoint \ - bin/iptb bin/go-sleep + bin/iptb bin/go-sleep bin/random-files SHARNESS = lib/sharness/sharness.sh IPFS_ROOT = ../.. diff --git a/test/sharness/lib/random-dep.go b/test/sharness/lib/random-dep.go index 643f08ea86a..94b758d7bd4 100644 --- a/test/sharness/lib/random-dep.go +++ b/test/sharness/lib/random-dep.go @@ -5,4 +5,5 @@ package randomdep import ( _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random" + _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-random-files" ) From 5f59556ff9275db62c1aaf0e18cf0913960fafec Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 29 Jul 2015 04:06:48 -0700 Subject: [PATCH 6/6] add -w comprehensive tests made many more tests for ipfs add -w License: MIT Signed-off-by: Juan Batiz-Benet --- test/sharness/t0040-add-and-cat.sh | 22 ----- test/sharness/t0043-add-w.sh | 148 +++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 22 deletions(-) create mode 100755 test/sharness/t0043-add-w.sh diff --git a/test/sharness/t0040-add-and-cat.sh b/test/sharness/t0040-add-and-cat.sh index 148548616fc..ef74be1c4ab 100755 --- a/test/sharness/t0040-add-and-cat.sh +++ b/test/sharness/t0040-add-and-cat.sh @@ -212,28 +212,6 @@ test_expect_success "'ipfs cat' output looks good" ' test_cmp mountdir/bigfile actual ' -test_expect_success "ipfs add -w succeeds" ' - ipfs add -w mountdir/hello.txt >actual -' - -test_expect_success "ipfs add -w output looks good" ' - HASH_W1="QmVr26fY1tKyspEJBniVhqxQeEjhF78XerGiqWAwraVLQH" && - HASH_W2="QmVJfrqd4ogGZME6LWkkikAGddYgh9dBs2U14DHZZUBk7W" && - echo "added $HASH_W1 mountdir/hello.txt" >expected && - echo "added $HASH_W2 " >>expected && - test_cmp expected actual -' - -test_expect_success "ipfs add -w succeeds (dir)" ' - ipfs add -r -w mountdir | tail -n1 >actual -' - -test_expect_success "ipfs add -w output looks good (dir)" ' - HASH_W="Qmc341yGztU1o8n3c1u5xTYF3uE3zPPP2NYemG9MKz775V" && - echo "added $HASH_W " >expected && - test_cmp expected actual -' - test_expect_success FUSE "cat ipfs/bigfile succeeds" ' cat "ipfs/$HASH" >actual ' diff --git a/test/sharness/t0043-add-w.sh b/test/sharness/t0043-add-w.sh new file mode 100755 index 00000000000..5719aecb9d1 --- /dev/null +++ b/test/sharness/t0043-add-w.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# +# Copyright (c) 2014 Christian Couder +# MIT Licensed; see the LICENSE file in this repository. +# + +test_description="Test add -w" + +add_w_m='QmazHkwx6mPmmCEi1jR5YzjjQd1g5XzKfYQLzRAg7x5uUk' + +add_w_1='added Qme987pqNBhZZXy4ckeXiR7zaRQwBabB7fTgHurW2yJfNu m/4r93 +added Qmf82PSsMpUHcrqxa69KG6Qp5yeK7K9BTizXgG3nvzWcNG ' + +add_w_12='added Qme987pqNBhZZXy4ckeXiR7zaRQwBabB7fTgHurW2yJfNu m/4r93 +added QmVb4ntSZZnT2J2zvCmXKMJc52cmZYH6AB37MzeYewnkjs m/4u6ead +added QmZPASVB6EsADrLN8S2sak34zEHL8mx4TAVsPJU9cNnQQJ ' + +add_w_21='added QmVb4ntSZZnT2J2zvCmXKMJc52cmZYH6AB37MzeYewnkjs m/4u6ead +added Qme987pqNBhZZXy4ckeXiR7zaRQwBabB7fTgHurW2yJfNu m/4r93 +added QmZPASVB6EsADrLN8S2sak34zEHL8mx4TAVsPJU9cNnQQJ ' + +add_w_d1='added QmPcaX84tDiTfzdTn8GQxexodgeWH6mHjSss5Zfr5ojssb m/t_1wp-8a2/_jo7/-s782qgs +added QmaVBqquUuXKjkyWHXaXfsaQUxAnsCKS95VRDHU8PzGA4K m/t_1wp-8a2/_jo7/15totauzkak- +added QmaAHFG8cmhW3WLjofx5siSp44VV25ETN6ThzrU8iAqpkR m/t_1wp-8a2/_jo7/galecuirrj4r +added QmeuSfhJNKwBESp1W9H8cfoMdBfW3AeHQDWXbNXQJYWp53 m/t_1wp-8a2/_jo7/mzo50r-1xidf5zx +added QmYC3u5jGWuyFwvTxtvLYm2K3SpWZ31tg3NjpVVvh9cJaJ m/t_1wp-8a2/_jo7/wzvsihy +added QmQkib3f9XNX5sj6WEahLUPFpheTcwSRJwUCSvjcv8b9by m/t_1wp-8a2/_jo7 +added QmNQoesMj1qp8ApE51NbtTjFYksyzkezPD4cat7V2kzbKN ' + +add_w_d2='added QmVaKAt2eVftNKFfKhiBV7Mu5HjCugffuLqWqobSSFgiA7 m/t_1wp-8a2/h3qpecj0 +added QmU9Jqks8TPu4vFr6t7EKkAKQrSJuEujNj1AkzoCeTEDFJ m/ha6f0x7su6/gnz66h/1k0xpx34 +added QmSLYZycXAufRw3ePMVH2brbtYWCcWsmksGLbHcT8ia9Ke m/ha6f0x7su6/gnz66h/9cwudvacx +added QmfYmpCCAMU9nLe7xbrYsHf5z2R2GxeQnsm4zavUhX9vq2 m/ha6f0x7su6/gnz66h/9ximv51cbo8 +added QmWgEE4e2kfx3b8HZcBk5cLrfhoi8kTMQP2MipgPhykuV3 m/ha6f0x7su6/gnz66h/b54ygh6gs +added QmcLbqEqhREGednc6mrVtanee4WHKp5JnUfiwTTHCJwuDf m/ha6f0x7su6/gnz66h/lbl5 +added QmVPwNy8pZegpsNmsjjZvdTQn4uCeuZgtzhgWhRSQWjK9x m/ha6f0x7su6/gnz66h +added QmPcaX84tDiTfzdTn8GQxexodgeWH6mHjSss5Zfr5ojssb m/t_1wp-8a2/_jo7/-s782qgs +added QmaVBqquUuXKjkyWHXaXfsaQUxAnsCKS95VRDHU8PzGA4K m/t_1wp-8a2/_jo7/15totauzkak- +added QmaAHFG8cmhW3WLjofx5siSp44VV25ETN6ThzrU8iAqpkR m/t_1wp-8a2/_jo7/galecuirrj4r +added QmeuSfhJNKwBESp1W9H8cfoMdBfW3AeHQDWXbNXQJYWp53 m/t_1wp-8a2/_jo7/mzo50r-1xidf5zx +added QmYC3u5jGWuyFwvTxtvLYm2K3SpWZ31tg3NjpVVvh9cJaJ m/t_1wp-8a2/_jo7/wzvsihy +added QmQkib3f9XNX5sj6WEahLUPFpheTcwSRJwUCSvjcv8b9by m/t_1wp-8a2/_jo7 +added Qme987pqNBhZZXy4ckeXiR7zaRQwBabB7fTgHurW2yJfNu m/4r93 +added QmTmc46fhKC8Liuh5soy1VotdnHcqLu3r6HpPGwDZCnqL1 ' + +add_w_r='QmWpSjVaMts6cXr4g4uQ9AVadunLKxW7Fhyxk3TXo36hEf' + +. lib/test-lib.sh + +test_add_w() { + + test_expect_success "go-random-files is installed" ' + type random-files + ' + + test_expect_success "random-files generates test files" ' + random-files --seed 7547632 --files 5 --dirs 2 --depth 3 m && + echo "$add_w_m" >expected && + ipfs add -q -r m | tail -n1 >actual && + test_cmp expected actual + ' + + # test single file + test_expect_success "ipfs add -w (single file) succeeds" ' + ipfs add -w m/4r93 >actual + ' + + test_expect_success "ipfs add -w (single file) is correct" ' + echo "$add_w_1" >expected && + test_cmp expected actual + ' + + # test two files together + test_expect_success "ipfs add -w (multiple) succeeds" ' + ipfs add -w m/4r93 m/4u6ead >actual + ' + + test_expect_success "ipfs add -w (multiple) is correct" ' + echo "$add_w_12" >expected && + test_cmp expected actual + ' + + test_expect_success "ipfs add -w (multiple) succeeds" ' + ipfs add -w m/4u6ead m/4r93 >actual + ' + + test_expect_success "ipfs add -w (multiple) orders" ' + echo "$add_w_21" >expected && + test_cmp expected actual + ' + + # test a directory + test_expect_success "ipfs add -w -r (dir) succeeds" ' + ipfs add -r -w m/t_1wp-8a2/_jo7 >actual + ' + + test_expect_success "ipfs add -w -r (dir) is correct" ' + echo "$add_w_d1" >expected && + test_cmp expected actual + ' + + # test files and directory + test_expect_success "ipfs add -w -r succeeds" ' + ipfs add -w -r m/t_1wp-8a2/h3qpecj0 \ + m/ha6f0x7su6/gnz66h m/t_1wp-8a2/_jo7 m/4r93 >actual + ' + + test_expect_success "ipfs add -w -r is correct" ' + echo "$add_w_d2" >expected && + test_cmp expected actual + ' + + # test -w -r m/* == -r m + test_expect_success "ipfs add -w -r m/* == add -r m succeeds" ' + ipfs add -q -w -r m/* | tail -n1 >actual + ' + + test_expect_success "ipfs add -w -r m/* == add -r m is correct" ' + echo "$add_w_m" >expected && + test_cmp expected actual + ' + + # test repeats together + test_expect_success "ipfs add -w (repeats) succeeds" ' + ipfs add -q -w -r m/t_1wp-8a2/h3qpecj0 m/ha6f0x7su6/gnz66h \ + m/t_1wp-8a2/_jo7 m/4r93 m/t_1wp-8a2 m/t_1wp-8a2 m/4r93 \ + m/4r93 m/ha6f0x7su6/_rwujlf3qh_g08 \ + m/ha6f0x7su6/gnz66h/9cwudvacx | tail -n1 >actual + ' + + test_expect_success "ipfs add -w (repeats) is correct" ' + echo "$add_w_r" >expected && + test_cmp expected actual + ' + +} + +test_init_ipfs + +test_add_w + +test_launch_ipfs_daemon + +test_add_w + +test_kill_ipfs_daemon + +test_done