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