]> Sergey Matveev's repositories - btrtrc.git/blob - fs/torrentfs_test.go
Add piece blob torrent.Data storage, and move testutil to internal/, add basic transf...
[btrtrc.git] / fs / torrentfs_test.go
1 package torrentfs
2
3 import (
4         "bytes"
5         "fmt"
6         "io/ioutil"
7         "net"
8         "net/http"
9         _ "net/http/pprof"
10         "os"
11         "path/filepath"
12         "strconv"
13         "strings"
14         "testing"
15         "time"
16
17         "golang.org/x/net/context"
18
19         "bitbucket.org/anacrolix/go.torrent"
20         "bitbucket.org/anacrolix/go.torrent/data/mmap"
21         "bitbucket.org/anacrolix/go.torrent/internal/testutil"
22         "bitbucket.org/anacrolix/go.torrent/util"
23
24         "github.com/anacrolix/libtorgo/metainfo"
25
26         "bazil.org/fuse"
27         fusefs "bazil.org/fuse/fs"
28 )
29
30 func TestTCPAddrString(t *testing.T) {
31         l, err := net.Listen("tcp4", "localhost:0")
32         if err != nil {
33                 t.Fatal(err)
34         }
35         defer l.Close()
36         c, err := net.Dial("tcp", l.Addr().String())
37         if err != nil {
38                 t.Fatal(err)
39         }
40         defer c.Close()
41         ras := c.RemoteAddr().String()
42         ta := &net.TCPAddr{
43                 IP:   net.IPv4(127, 0, 0, 1),
44                 Port: util.AddrPort(l.Addr()),
45         }
46         s := ta.String()
47         if ras != s {
48                 t.FailNow()
49         }
50 }
51
52 type testLayout struct {
53         BaseDir   string
54         MountDir  string
55         Completed string
56         Metainfo  *metainfo.MetaInfo
57 }
58
59 func (me *testLayout) Destroy() error {
60         return os.RemoveAll(me.BaseDir)
61 }
62
63 func newGreetingLayout() (tl testLayout, err error) {
64         tl.BaseDir, err = ioutil.TempDir("", "torrentfs")
65         if err != nil {
66                 return
67         }
68         tl.Completed = filepath.Join(tl.BaseDir, "completed")
69         os.Mkdir(tl.Completed, 0777)
70         tl.MountDir = filepath.Join(tl.BaseDir, "mnt")
71         os.Mkdir(tl.MountDir, 0777)
72         name := testutil.CreateDummyTorrentData(tl.Completed)
73         metaInfoBuf := &bytes.Buffer{}
74         testutil.CreateMetaInfo(name, metaInfoBuf)
75         tl.Metainfo, err = metainfo.Load(metaInfoBuf)
76         return
77 }
78
79 // Unmount without first killing the FUSE connection while there are FUSE
80 // operations blocked inside the filesystem code.
81 func TestUnmountWedged(t *testing.T) {
82         layout, err := newGreetingLayout()
83         if err != nil {
84                 t.Fatal(err)
85         }
86         defer func() {
87                 err := layout.Destroy()
88                 if err != nil {
89                         t.Log(err)
90                 }
91         }()
92         client, err := torrent.NewClient(&torrent.Config{
93                 DataDir:         filepath.Join(layout.BaseDir, "incomplete"),
94                 DisableTrackers: true,
95                 NoDHT:           true,
96
97                 NoDefaultBlocklist: true,
98         })
99         defer client.Stop()
100         client.AddTorrent(layout.Metainfo)
101         fs := New(client)
102         fuseConn, err := fuse.Mount(layout.MountDir)
103         if err != nil {
104                 if strings.Contains(err.Error(), "fuse") {
105                         t.Skip(err)
106                 }
107                 t.Fatal(err)
108         }
109         go func() {
110                 server := fusefs.Server{
111                         FS: fs,
112                         Debug: func(msg interface{}) {
113                                 t.Log(msg)
114                         },
115                 }
116                 server.Serve(fuseConn)
117         }()
118         <-fuseConn.Ready
119         if err := fuseConn.MountError; err != nil {
120                 t.Fatal(err)
121         }
122         // Read the greeting file, though it will never be available. This should
123         // "wedge" FUSE, requiring the fs object to be forcibly destroyed. The
124         // read call will return with a FS error.
125         go func() {
126                 _, err := ioutil.ReadFile(filepath.Join(layout.MountDir, layout.Metainfo.Info.Name))
127                 if err == nil {
128                         t.Fatal("expected error reading greeting")
129                 }
130         }()
131
132         // Wait until the read has blocked inside the filesystem code.
133         fs.mu.Lock()
134         for fs.blockedReads != 1 {
135                 fs.event.Wait()
136         }
137         fs.mu.Unlock()
138
139         fs.Destroy()
140
141         for {
142                 err = fuse.Unmount(layout.MountDir)
143                 if err != nil {
144                         t.Logf("error unmounting: %s", err)
145                         time.Sleep(time.Millisecond)
146                 } else {
147                         break
148                 }
149         }
150
151         err = fuseConn.Close()
152         if err != nil {
153                 t.Fatalf("error closing fuse conn: %s", err)
154         }
155 }
156
157 func TestDownloadOnDemand(t *testing.T) {
158         layout, err := newGreetingLayout()
159         if err != nil {
160                 t.Fatal(err)
161         }
162         defer layout.Destroy()
163         seeder, err := torrent.NewClient(&torrent.Config{
164                 DataDir:         layout.Completed,
165                 DisableTrackers: true,
166                 NoDHT:           true,
167                 ListenAddr:      ":0",
168
169                 NoDefaultBlocklist: true,
170                 // Ensure that the metainfo is obtained over the wire, since we added
171                 // the torrent to the seeder by magnet.
172                 DisableMetainfoCache: true,
173         })
174         if err != nil {
175                 t.Fatalf("error creating seeder client: %s", err)
176         }
177         seeder.SetIPBlockList(nil)
178         defer seeder.Stop()
179         http.HandleFunc("/seeder", func(w http.ResponseWriter, req *http.Request) {
180                 seeder.WriteStatus(w)
181         })
182         _, err = seeder.AddMagnet(fmt.Sprintf("magnet:?xt=urn:btih:%x", layout.Metainfo.Info.Hash))
183         if err != nil {
184                 t.Fatal(err)
185         }
186         leecher, err := torrent.NewClient(&torrent.Config{
187                 DisableTrackers: true,
188                 NoDHT:           true,
189                 ListenAddr:      ":0",
190                 DisableTCP:      true,
191
192                 NoDefaultBlocklist: true,
193
194                 TorrentDataOpener: func(info *metainfo.Info) (torrent.Data, error) {
195                         return mmap.TorrentData(info, filepath.Join(layout.BaseDir, "download"))
196                 },
197
198                 // This can be used to check if clients can connect to other clients
199                 // with the same ID.
200
201                 // PeerID: seeder.PeerID(),
202         })
203         leecher.SetIPBlockList(nil)
204         http.HandleFunc("/leecher", func(w http.ResponseWriter, req *http.Request) {
205                 leecher.WriteStatus(w)
206         })
207         defer leecher.Stop()
208         leecher.AddTorrent(layout.Metainfo)
209         var ih torrent.InfoHash
210         util.CopyExact(ih[:], layout.Metainfo.Info.Hash)
211         leecher.AddPeers(ih, []torrent.Peer{func() torrent.Peer {
212                 _, port, err := net.SplitHostPort(seeder.ListenAddr().String())
213                 if err != nil {
214                         panic(err)
215                 }
216                 portInt64, err := strconv.ParseInt(port, 0, 0)
217                 if err != nil {
218                         panic(err)
219                 }
220                 return torrent.Peer{
221                         IP: func() net.IP {
222                                 ret, _ := net.ResolveIPAddr("ip", "localhost")
223                                 return ret.IP
224                         }(),
225                         Port: int(portInt64),
226                 }
227         }()})
228         fs := New(leecher)
229         defer fs.Destroy()
230         root, _ := fs.Root()
231         node, _ := root.(fusefs.NodeStringLookuper).Lookup(context.Background(), "greeting")
232         size := int(node.Attr().Size)
233         resp := &fuse.ReadResponse{
234                 Data: make([]byte, size),
235         }
236         node.(fusefs.HandleReader).Read(context.Background(), &fuse.ReadRequest{
237                 Size: size,
238         }, resp)
239         content := resp.Data
240         if string(content) != testutil.GreetingFileContents {
241                 t.FailNow()
242         }
243 }