github.com/anacrolix/torrent@v1.61.0/fs/stream-sintel_test.go (about)

     1  //go:build !windows
     2  
     3  package torrentfs_test
     4  
     5  import (
     6  	"context"
     7  	"crypto/md5"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/fs"
    13  	"os"
    14  	"os/signal"
    15  	"path/filepath"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/anacrolix/fuse"
    20  	fusefs "github.com/anacrolix/fuse/fs"
    21  	"github.com/anacrolix/missinggo/v2/panicif"
    22  	"github.com/go-quicktest/qt"
    23  	"golang.org/x/sync/errgroup"
    24  
    25  	"github.com/anacrolix/torrent"
    26  	torrentfs "github.com/anacrolix/torrent/fs"
    27  	"github.com/anacrolix/torrent/internal/testutil"
    28  	"github.com/anacrolix/torrent/metainfo"
    29  )
    30  
    31  func copyFile(src, dst string) (err error) {
    32  	from, err := os.Open(src)
    33  	if err != nil {
    34  		return
    35  	}
    36  	defer from.Close()
    37  	to, err := os.Create(dst)
    38  	if err != nil {
    39  		return
    40  	}
    41  	defer to.Close()
    42  	_, err = io.Copy(to, from)
    43  	if err != nil {
    44  		return
    45  	}
    46  	return to.Close()
    47  }
    48  
    49  func TestStreamSintelMagnet(t *testing.T) {
    50  	t.Skip("flaky and fuck this shit")
    51  	ctx := t.Context()
    52  	ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
    53  	defer cancel()
    54  	fileHashes := map[string]string{
    55  		"poster.jpg": "f9223791908131c505d7bdafa7a8aaf5",
    56  		"Sintel.mp4": "083e808d56aa7b146f513b3458658292",
    57  	}
    58  	targetFile := "Sintel.mp4"
    59  	if testing.Short() {
    60  		targetFile = "poster.jpg"
    61  	}
    62  	dir := t.TempDir()
    63  	t.Logf("temp dir: %v", dir)
    64  	metainfoDir := filepath.Join(dir, "torrents")
    65  	mountDir := filepath.Join(dir, "mnt")
    66  	sourceTorrentDir := `../testdata`
    67  	dummyTorrent := filepath.Join(sourceTorrentDir, `debian-10.8.0-amd64-netinst.iso.torrent`)
    68  	t.Log(os.Getwd())
    69  	dummyTorrentDst := filepath.Join(metainfoDir, filepath.Base(dummyTorrent))
    70  	err := os.MkdirAll(filepath.Dir(dummyTorrentDst), 0o700)
    71  	panicif.Err(err)
    72  	err = copyFile(dummyTorrent, dummyTorrentDst)
    73  	panicif.Err(err)
    74  	mi, err := metainfo.LoadFromFile(filepath.Join(sourceTorrentDir, `sintel.torrent`))
    75  	panicif.Err(err)
    76  	m, err := mi.MagnetV2()
    77  	panicif.Err(err)
    78  	err = os.WriteFile(filepath.Join(metainfoDir, "sintel.magnet"), []byte(m.String()), 0600)
    79  	panicif.Err(err)
    80  	cfg := torrent.NewDefaultClientConfig()
    81  	//cfg.Debug = true
    82  	cfg.ListenPort = 0
    83  	cl, err := torrent.NewClient(cfg)
    84  	panicif.Err(err)
    85  	testutil.ExportStatusWriter(cl, "", t)
    86  	defer cl.Close()
    87  
    88  	err = os.Mkdir(mountDir, 0700)
    89  	panicif.Err(err)
    90  	conn, err := fuse.Mount(mountDir)
    91  	panicif.Err(err)
    92  	t.Cleanup(func() { fuse.Unmount(mountDir) })
    93  	t.Cleanup(func() { conn.Close() })
    94  	fs := torrentfs.New(cl)
    95  	var eg errgroup.Group
    96  	eg.Go(func() (err error) {
    97  		err = fusefs.Serve(conn, fs)
    98  		if err != nil {
    99  			err = fmt.Errorf("serving fuse: %w", err)
   100  			t.Log(err)
   101  			return
   102  		}
   103  		return
   104  	})
   105  	<-conn.Ready
   106  	err = conn.MountError
   107  	if err != nil {
   108  		err = fmt.Errorf("conn mount error: %w", err)
   109  	}
   110  
   111  	go func() {
   112  		_, err := cl.AddTorrent(mi)
   113  		panicif.Err(err)
   114  		_, err = cl.AddMagnet(m.String())
   115  		panicif.Err(err)
   116  	}()
   117  
   118  	f, err := openFileWhenExists(t, filepath.Join(mountDir, "Sintel", targetFile))
   119  	panicif.Err(err)
   120  	t.Logf("opened %v", f.Name())
   121  	t.Cleanup(func() { f.Close() })
   122  
   123  	fi, err := f.Stat()
   124  	panicif.Err(err)
   125  
   126  	var written int64
   127  	w := writer{
   128  		onWrite: func(p []byte) (n int, err error) {
   129  			written += int64(len(p))
   130  			t.Logf("wrote %v bytes", len(p))
   131  			t.Logf("progress %v", float64(written)/float64(fi.Size()))
   132  			return len(p), nil
   133  		},
   134  	}
   135  	h := md5.New()
   136  	go func() {
   137  		<-ctx.Done()
   138  		f.Close()
   139  	}()
   140  	_, err = f.WriteTo(io.MultiWriter(h, &w))
   141  	if ctx.Err() != nil {
   142  		t.Fatal(ctx.Err())
   143  	}
   144  	panicif.Err(err)
   145  	err = f.Close()
   146  	panicif.Err(err)
   147  
   148  	qt.Assert(t, qt.Equals(hex.EncodeToString(h.Sum(nil)), fileHashes[targetFile]))
   149  
   150  	err = fuse.Unmount(mountDir)
   151  	panicif.Err(err)
   152  	err = eg.Wait()
   153  	panicif.Err(err)
   154  }
   155  
   156  func openFileWhenExists(t *testing.T, name string) (f *os.File, err error) {
   157  	ctx := t.Context()
   158  	for {
   159  		f, err = os.Open(name)
   160  		if err == nil {
   161  			return
   162  		}
   163  		if !errors.Is(err, fs.ErrNotExist) {
   164  			return
   165  		}
   166  		t.Logf("file does not yet exist: %v", name)
   167  		select {
   168  		case <-ctx.Done():
   169  			err = context.Cause(ctx)
   170  			return
   171  		case <-time.After(1 * time.Second):
   172  		}
   173  	}
   174  }
   175  
   176  type writer struct {
   177  	onWrite func(b []byte) (n int, err error)
   178  }
   179  
   180  func (w writer) Write(p []byte) (n int, err error) {
   181  	return w.onWrite(p)
   182  }