////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC                                           //
//                                                                            //
// Use of this source code is governed by a license that can be found in the  //
// LICENSE file                                                               //
////////////////////////////////////////////////////////////////////////////////

package fileTransfer

import (
	"bytes"
	"fmt"
	"github.com/pkg/errors"
	"gitlab.com/elixxir/client/interfaces"
	"gitlab.com/elixxir/client/storage/utility"
	"gitlab.com/elixxir/client/storage/versioned"
	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
	"gitlab.com/elixxir/ekv"
	"gitlab.com/elixxir/primitives/format"
	"gitlab.com/xx_network/primitives/id"
	"gitlab.com/xx_network/primitives/netTime"
	"math/rand"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

// Tests that NewSentTransfer creates the expected SentTransfer and that it is
// saved to storage.
func Test_NewSentTransfer(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	recipient, _ := id.NewRandomID(prng, id.User)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	kvPrefixed := kv.Prefix(makeSentTransferPrefix(tid))
	parts := [][]byte{
		[]byte("test0"), []byte("test1"), []byte("test2"),
		[]byte("test3"), []byte("test4"), []byte("test5"),
	}
	numParts, numFps := uint16(len(parts)), uint16(float64(len(parts))*1.5)
	fpVector, _ := utility.NewStateVector(
		kvPrefixed, sentFpVectorKey, uint32(numFps))
	inProgressStatus, _ := utility.NewStateVector(
		kvPrefixed, sentInProgressVectorKey, uint32(numParts))
	finishedStatus, _ := utility.NewStateVector(
		kvPrefixed, sentFinishedVectorKey, uint32(numParts))

	type cbFields struct {
		completed            bool
		sent, arrived, total uint16
		err                  error
	}

	expectedCB := cbFields{
		completed: false,
		sent:      0,
		arrived:   0,
		total:     numParts,
		err:       nil,
	}

	cbChan := make(chan cbFields)
	cb := func(completed bool, sent, arrived, total uint16,
		t interfaces.FilePartTracker, err error) {
		cbChan <- cbFields{
			completed: completed,
			sent:      sent,
			arrived:   arrived,
			total:     total,
			err:       err,
		}
	}

	expectedPeriod := time.Second

	expected := &SentTransfer{
		recipient: recipient,
		key:       key,
		numParts:  numParts,
		numFps:    numFps,
		fpVector:  fpVector,
		sentParts: &partStore{
			parts:    partSliceToMap(parts...),
			numParts: uint16(len(parts)),
			kv:       kvPrefixed,
		},
		inProgressTransfers: &transferredBundle{
			list: make(map[id.Round][]uint16),
			key:  inProgressKey,
			kv:   kvPrefixed,
		},
		finishedTransfers: &transferredBundle{
			list: make(map[id.Round][]uint16),
			key:  finishedKey,
			kv:   kvPrefixed,
		},
		inProgressStatus: inProgressStatus,
		finishedStatus:   finishedStatus,
		progressCallbacks: []*sentCallbackTracker{
			newSentCallbackTracker(cb, expectedPeriod),
		},
		status: Running,
		kv:     kvPrefixed,
	}

	// Create new SentTransfer
	st, err := NewSentTransfer(
		recipient, tid, key, parts, numFps, cb, expectedPeriod, kv)
	if err != nil {
		t.Errorf("NewSentTransfer returned an error: %+v", err)
	}

	// Check that the callback is called when added
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting fpr progress callback to be called.")
	case cbResults := <-cbChan:
		if !reflect.DeepEqual(expectedCB, cbResults) {
			t.Errorf("Did not receive correct results from callback."+
				"\nexpected: %+v\nreceived: %+v", expectedCB, cbResults)
		}
	}

	st.progressCallbacks = expected.progressCallbacks

	// Check that the new object matches the expected
	if !reflect.DeepEqual(expected, st) {
		t.Errorf("New SentTransfer does not match expected."+
			"\nexpected: %#v\nreceived: %#v", expected, st)
	}

	// Make sure it is saved to storage
	_, err = kvPrefixed.Get(sentTransferKey, sentTransferVersion)
	if err != nil {
		t.Errorf("Failed to get new SentTransfer from storage: %+v", err)
	}

	// Check that the fingerprint vector has correct values
	if st.fpVector.GetNumAvailable() != uint32(numFps) {
		t.Errorf("Incorrect number of available keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, st.fpVector.GetNumAvailable())
	}
	if st.fpVector.GetNumKeys() != uint32(numFps) {
		t.Errorf("Incorrect number of keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, st.fpVector.GetNumKeys())
	}
	if st.fpVector.GetNumUsed() != 0 {
		t.Errorf("Incorrect number of used keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", 0, st.fpVector.GetNumUsed())
	}
}

// Tests that SentTransfer.ReInit overwrites the fingerprint vector, in-progress
// transfer, finished transfers, and progress callbacks with new and empty
// objects.
func TestSentTransfer_ReInit(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	recipient, _ := id.NewRandomID(prng, id.User)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	kvPrefixed := kv.Prefix(makeSentTransferPrefix(tid))
	parts := [][]byte{
		[]byte("test0"), []byte("test1"), []byte("test2"),
		[]byte("test3"), []byte("test4"), []byte("test5"),
	}
	numParts, numFps1 := uint16(len(parts)), uint16(float64(len(parts))*1.5)
	numFps2 := 2 * numFps1
	fpVector, _ := utility.NewStateVector(
		kvPrefixed, sentFpVectorKey, uint32(numFps2))
	inProgressStatus, _ := utility.NewStateVector(
		kvPrefixed, sentInProgressVectorKey, uint32(numParts))
	finishedStatus, _ := utility.NewStateVector(
		kvPrefixed, sentFinishedVectorKey, uint32(numParts))

	type cbFields struct {
		completed            bool
		sent, arrived, total uint16
		err                  error
	}

	expectedCB := cbFields{
		completed: false,
		sent:      0,
		arrived:   0,
		total:     numParts,
		err:       nil,
	}

	cbChan := make(chan cbFields)
	cb := func(completed bool, sent, arrived, total uint16,
		t interfaces.FilePartTracker, err error) {
		cbChan <- cbFields{
			completed: completed,
			sent:      sent,
			arrived:   arrived,
			total:     total,
			err:       err,
		}
	}

	expectedPeriod := time.Millisecond

	expected := &SentTransfer{
		recipient: recipient,
		key:       key,
		numParts:  numParts,
		numFps:    numFps2,
		fpVector:  fpVector,
		sentParts: &partStore{
			parts:    partSliceToMap(parts...),
			numParts: uint16(len(parts)),
			kv:       kvPrefixed,
		},
		inProgressTransfers: &transferredBundle{
			list: make(map[id.Round][]uint16),
			key:  inProgressKey,
			kv:   kvPrefixed,
		},
		finishedTransfers: &transferredBundle{
			list: make(map[id.Round][]uint16),
			key:  finishedKey,
			kv:   kvPrefixed,
		},
		inProgressStatus: inProgressStatus,
		finishedStatus:   finishedStatus,
		progressCallbacks: []*sentCallbackTracker{
			newSentCallbackTracker(cb, expectedPeriod),
		},
		status: Running,
		kv:     kvPrefixed,
	}

	// Create new SentTransfer
	st, err := NewSentTransfer(
		recipient, tid, key, parts, numFps1, nil, 2*expectedPeriod, kv)
	if err != nil {
		t.Errorf("NewSentTransfer returned an error: %+v", err)
	}

	// Re-initialize SentTransfer with new number of fingerprints and callback
	err = st.ReInit(numFps2, cb, expectedPeriod)
	if err != nil {
		t.Errorf("ReInit returned an error: %+v", err)
	}

	// Check that the callback is called when added
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting fpr progress callback to be called.")
	case cbResults := <-cbChan:
		if !reflect.DeepEqual(expectedCB, cbResults) {
			t.Errorf("Did not receive correct results from callback."+
				"\nexpected: %+v\nreceived: %+v", expectedCB, cbResults)
		}
	}

	st.progressCallbacks = expected.progressCallbacks

	// Check that the new object matches the expected
	if !reflect.DeepEqual(expected, st) {
		t.Errorf("New SentTransfer does not match expected."+
			"\nexpected: %#v\nreceived: %#v", expected, st)
	}

	// Make sure it is saved to storage
	_, err = kvPrefixed.Get(sentTransferKey, sentTransferVersion)
	if err != nil {
		t.Errorf("Failed to get new SentTransfer from storage: %+v", err)
	}

	// Check that the fingerprint vector has correct values
	if st.fpVector.GetNumAvailable() != uint32(numFps2) {
		t.Errorf("Incorrect number of available keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps2, st.fpVector.GetNumAvailable())
	}
	if st.fpVector.GetNumKeys() != uint32(numFps2) {
		t.Errorf("Incorrect number of keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps2, st.fpVector.GetNumKeys())
	}
	if st.fpVector.GetNumUsed() != 0 {
		t.Errorf("Incorrect number of used keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", 0, st.fpVector.GetNumUsed())
	}
}

// Tests that SentTransfer.GetRecipient returns the expected ID.
func TestSentTransfer_GetRecipient(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedRecipient, _ := id.NewRandomID(prng, id.User)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)

	// Create new SentTransfer
	st, err := NewSentTransfer(
		expectedRecipient, tid, key, [][]byte{}, 5, nil, 0, kv)
	if err != nil {
		t.Errorf("Failed to create new SentTransfer: %+v", err)
	}

	if expectedRecipient != st.GetRecipient() {
		t.Errorf("Failed to get expected transfer key."+
			"\nexpected: %s\nreceived: %s", expectedRecipient, st.GetRecipient())
	}
}

// Tests that SentTransfer.GetTransferKey returns the expected transfer key.
func TestSentTransfer_GetTransferKey(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	recipient, _ := id.NewRandomID(prng, id.User)
	tid, _ := ftCrypto.NewTransferID(prng)
	expectedKey, _ := ftCrypto.NewTransferKey(prng)

	// Create new SentTransfer
	st, err := NewSentTransfer(
		recipient, tid, expectedKey, [][]byte{}, 5, nil, 0, kv)
	if err != nil {
		t.Errorf("Failed to create new SentTransfer: %+v", err)
	}

	if expectedKey != st.GetTransferKey() {
		t.Errorf("Failed to get expected transfer key."+
			"\nexpected: %s\nreceived: %s", expectedKey, st.GetTransferKey())
	}
}

// Tests that SentTransfer.GetNumParts returns the expected number of parts.
func TestSentTransfer_GetNumParts(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedNumParts := uint16(16)
	_, st := newRandomSentTransfer(expectedNumParts, 24, kv, t)

	if expectedNumParts != st.GetNumParts() {
		t.Errorf("Failed to get expected number of parts."+
			"\nexpected: %d\nreceived: %d", expectedNumParts, st.GetNumParts())
	}
}

// Tests that SentTransfer.GetNumFps returns the expected number of
// fingerprints.
func TestSentTransfer_GetNumFps(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedNumFps := uint16(24)
	_, st := newRandomSentTransfer(16, expectedNumFps, kv, t)

	if expectedNumFps != st.GetNumFps() {
		t.Errorf("Failed to get expected number of fingerprints."+
			"\nexpected: %d\nreceived: %d", expectedNumFps, st.GetNumFps())
	}
}

// Tests that SentTransfer.GetNumAvailableFps returns the expected number of
// available fingerprints.
func TestSentTransfer_GetNumAvailableFps(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts, numFps := uint16(16), uint16(24)
	_, st := newRandomSentTransfer(numParts, numFps, kv, t)

	if numFps != st.GetNumAvailableFps() {
		t.Errorf("Failed to get expected number of available fingerprints."+
			"\nexpected: %d\nreceived: %d",
			numFps, st.GetNumAvailableFps())
	}

	for i := uint16(0); i < numParts; i++ {
		_, _ = st.fpVector.Next()
	}

	if numFps-numParts != st.GetNumAvailableFps() {
		t.Errorf("Failed to get expected number of available fingerprints."+
			"\nexpected: %d\nreceived: %d",
			numFps-numParts, st.GetNumAvailableFps())
	}
}

// Tests that SentTransfer.GetStatus returns the expected status at each stage
// of the transfer.
func TestSentTransfer_GetStatus(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts, numFps := uint16(2), uint16(4)
	_, st := newRandomSentTransfer(numParts, numFps, kv, t)

	status := st.GetStatus()
	if status != Running {
		t.Errorf("Unexpected transfer status.\nexpected: %s\nreceived: %s",
			Running, status)
	}

	_, _ = st.SetInProgress(0, 0, 1)
	_, _ = st.FinishTransfer(0)

	status = st.GetStatus()
	if status != Stopping {
		t.Errorf("Unexpected transfer status.\nexpected: %s\nreceived: %s",
			Stopping, status)
	}

	st.CallProgressCB(nil)

	status = st.GetStatus()
	if status != Stopped {
		t.Errorf("Unexpected transfer status.\nexpected: %s\nreceived: %s",
			Stopped, status)
	}
}

// Tests that SentTransfer.IsPartInProgress returns false before a part is set
// as in-progress and true after it is set via SentTransfer.SetInProgress. Also
// tests that it returns false after the part has been unset via
// SentTransfer.UnsetInProgress.
func TestSentTransfer_IsPartInProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(0)
	partNum := uint16(7)

	// Test that the part has not been set to in-progress
	if st.IsPartInProgress(partNum) {
		t.Errorf("Part number %d set as in-progress.", partNum)
	}

	// Set the part number to in-progress
	_, _ = st.SetInProgress(rid, partNum)

	// Test that the part has been set to in-progress
	if !st.IsPartInProgress(partNum) {
		t.Errorf("Part number %d not set as in-progress.", partNum)
	}

	// Unset the part as in-progress
	_, _ = st.UnsetInProgress(rid)

	// Test that the part has been unset
	if st.IsPartInProgress(partNum) {
		t.Errorf("Part number %d set as in-progress.", partNum)
	}
}

// Tests that SentTransfer.IsPartFinished returns false before a part is set as
// finished and true after it is set via SentTransfer.FinishTransfer.
func TestSentTransfer_IsPartFinished(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(0)
	partNum := uint16(7)

	// Set the part number to in-progress
	_, _ = st.SetInProgress(rid, partNum)

	// Test that the part has not been set to finished
	if st.IsPartFinished(partNum) {
		t.Errorf("Part number %d set as finished.", partNum)
	}

	// Set the part number to finished
	_, _ = st.FinishTransfer(rid)

	// Test that the part has been set to finished
	if !st.IsPartFinished(partNum) {
		t.Errorf("Part number %d not set as finished.", partNum)
	}
}

// Tests that SentTransfer.GetProgress returns the expected progress metrics for
// various transfer states.
func TestSentTransfer_GetProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts := uint16(16)
	_, st := newRandomSentTransfer(16, 24, kv, t)

	completed, sent, arrived, total, track := st.GetProgress()
	err := checkSentProgress(
		completed, sent, arrived, total, false, 0, 0, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, nil, nil, t)

	_, _ = st.SetInProgress(1, 0, 1, 2)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(completed, sent, arrived, total, false, 3, 0, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, []uint16{0, 1, 2}, nil, t)

	_, _ = st.SetInProgress(2, 3, 4, 5)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(completed, sent, arrived, total, false, 6, 0, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, []uint16{0, 1, 2, 3, 4, 5}, nil, t)

	_, _ = st.FinishTransfer(1)
	_, _ = st.UnsetInProgress(2)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(completed, sent, arrived, total, false, 0, 3, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, nil, []uint16{0, 1, 2}, t)

	_, _ = st.SetInProgress(3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(
		completed, sent, arrived, total, false, 10, 3, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts,
		[]uint16{6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []uint16{0, 1, 2}, t)

	_, _ = st.FinishTransfer(3)
	_, _ = st.SetInProgress(4, 3, 4, 5)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(
		completed, sent, arrived, total, false, 3, 13, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, []uint16{3, 4, 5},
		[]uint16{0, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, t)

	_, _ = st.FinishTransfer(4)

	completed, sent, arrived, total, track = st.GetProgress()
	err = checkSentProgress(completed, sent, arrived, total, true, 0, 16, numParts)
	if err != nil {
		t.Error(err)
	}
	checkSentTracker(track, st.numParts, nil,
		[]uint16{0, 1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 3, 4, 5}, t)
}

// Tests that 5 different callbacks all receive the expected data when
// SentTransfer.CallProgressCB is called at different stages of transfer.
func TestSentTransfer_CallProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	type progressResults struct {
		completed            bool
		sent, arrived, total uint16
		err                  error
	}

	period := time.Millisecond

	wg := sync.WaitGroup{}
	var step0, step1, step2, step3 uint64
	numCallbacks := 5

	for i := 0; i < numCallbacks; i++ {
		progressChan := make(chan progressResults)

		cbFunc := func(completed bool, sent, arrived, total uint16,
			t interfaces.FilePartTracker, err error) {
			progressChan <- progressResults{completed, sent, arrived, total, err}
		}
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			n := 0
			for {
				select {
				case <-time.NewTimer(time.Second).C:
					t.Errorf("Timed out after %s waiting for callback (%d).",
						period*5, i)
					return
				case r := <-progressChan:
					switch n {
					case 0:
						if err := checkSentProgress(r.completed, r.sent, r.arrived,
							r.total, false, 0, 0, st.numParts); err != nil {
							t.Errorf("%2d: %+v", i, err)
						}
						atomic.AddUint64(&step0, 1)
					case 1:
						if err := checkSentProgress(r.completed, r.sent, r.arrived,
							r.total, false, 0, 0, st.numParts); err != nil {
							t.Errorf("%2d: %+v", i, err)
						}
						atomic.AddUint64(&step1, 1)
					case 2:
						if err := checkSentProgress(r.completed, r.sent, r.arrived,
							r.total, false, 0, 6, st.numParts); err != nil {
							t.Errorf("%2d: %+v", i, err)
						}
						atomic.AddUint64(&step2, 1)
					case 3:
						if err := checkSentProgress(r.completed, r.sent, r.arrived,
							r.total, true, 0, 16, st.numParts); err != nil {
							t.Errorf("%2d: %+v", i, err)
						}
						atomic.AddUint64(&step3, 1)
						return
					default:
						t.Errorf("n (%d) is great than 3 (%d)", n, i)
						return
					}
					n++
				}
			}
		}(i)

		st.AddProgressCB(cbFunc, period)
	}

	for !atomic.CompareAndSwapUint64(&step0, uint64(numCallbacks), 0) {
	}

	st.CallProgressCB(nil)

	for !atomic.CompareAndSwapUint64(&step1, uint64(numCallbacks), 0) {
	}

	_, _ = st.SetInProgress(0, 0, 1, 2)
	_, _ = st.SetInProgress(1, 3, 4, 5)
	_, _ = st.SetInProgress(2, 6, 7, 8)
	_, _ = st.UnsetInProgress(1)
	_, _ = st.FinishTransfer(0)
	_, _ = st.FinishTransfer(2)

	st.CallProgressCB(nil)

	for !atomic.CompareAndSwapUint64(&step2, uint64(numCallbacks), 0) {
	}

	_, _ = st.SetInProgress(4, 3, 4, 5, 9, 10, 11, 12, 13, 14, 15)
	_, _ = st.FinishTransfer(4)

	st.CallProgressCB(nil)

	wg.Wait()
}

// Tests that SentTransfer.StopScheduledProgressCB stops a scheduled callback
// from being triggered.
func TestSentTransfer_StopScheduledProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	cbChan := make(chan struct{}, 5)
	cbFunc := interfaces.SentProgressCallback(
		func(completed bool, sent, arrived, total uint16,
			t interfaces.FilePartTracker, err error) {
			cbChan <- struct{}{}
		})
	st.AddProgressCB(cbFunc, 150*time.Millisecond)
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting for callback.")
	case <-cbChan:
	}

	st.CallProgressCB(nil)
	st.CallProgressCB(nil)
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting for callback.")
	case <-cbChan:
	}

	err := st.StopScheduledProgressCB()
	if err != nil {
		t.Errorf("StopScheduledProgressCB returned an error: %+v", err)
	}

	select {
	case <-time.NewTimer(200 * time.Millisecond).C:
	case <-cbChan:
		t.Error("Callback called when it should have been stopped.")
	}
}

// Tests that SentTransfer.AddProgressCB adds an item to the progress callback
// list.
func TestSentTransfer_AddProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	type callbackResults struct {
		completed            bool
		sent, arrived, total uint16
		err                  error
	}
	cbChan := make(chan callbackResults)
	cbFunc := interfaces.SentProgressCallback(
		func(completed bool, sent, arrived, total uint16,
			t interfaces.FilePartTracker, err error) {
			cbChan <- callbackResults{completed, sent, arrived, total, err}
		})

	done := make(chan bool)
	go func() {
		select {
		case <-time.NewTimer(time.Millisecond).C:
			t.Error("Timed out waiting for progress callback to be called.")
		case r := <-cbChan:
			err := checkSentProgress(
				r.completed, r.sent, r.arrived, r.total, false, 0, 0, 16)
			if err != nil {
				t.Error(err)
			}
			if r.err != nil {
				t.Errorf("Callback returned an error: %+v", err)
			}
		}
		done <- true
	}()

	period := time.Millisecond
	st.AddProgressCB(cbFunc, period)

	if len(st.progressCallbacks) != 1 {
		t.Errorf("Callback list should only have one item."+
			"\nexpected: %d\nreceived: %d", 1, len(st.progressCallbacks))
	}

	if st.progressCallbacks[0].period != period {
		t.Errorf("Callback has wrong lastCall.\nexpected: %s\nreceived: %s",
			period, st.progressCallbacks[0].period)
	}

	if st.progressCallbacks[0].lastCall != (time.Time{}) {
		t.Errorf("Callback has wrong time.\nexpected: %s\nreceived: %s",
			time.Time{}, st.progressCallbacks[0].lastCall)
	}

	if st.progressCallbacks[0].scheduled {
		t.Errorf("Callback has wrong scheduled.\nexpected: %t\nreceived: %t",
			false, st.progressCallbacks[0].scheduled)
	}
	<-done
}

// Loops through each file part encrypting it with SentTransfer.GetEncryptedPart
// and tests that it returns an encrypted part, MAC, and padding (nonce) that
// can be used to successfully decrypt and get the original part. Also tests
// that fingerprints are valid and not used more than once.
// It also tests that
func TestSentTransfer_GetEncryptedPart(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)
	prng := NewPrng(42)

	// Create and fill fingerprint map used to check fingerprint validity
	// The first item in the uint16 slice is the fingerprint number and the
	// second item is the number of times it has been used
	fpMap := make(map[format.Fingerprint][]uint16, st.numFps)
	for num, fp := range ftCrypto.GenerateFingerprints(st.key, st.numFps) {
		fpMap[fp] = []uint16{uint16(num), 0}
	}

	for i := uint16(0); i < st.numFps; i++ {
		partNum := i % st.numParts

		encPart, mac, padding, fp, err := st.GetEncryptedPart(partNum, 16, prng)
		if err != nil {
			t.Fatalf("GetEncryptedPart returned an error for part number "+
				"%d (%d): %+v", partNum, i, err)
		}

		// Check that the fingerprint is valid
		fpNum, exists := fpMap[fp]
		if !exists {
			t.Errorf("Fingerprint %s invalid for part number %d (%d).",
				fp, partNum, i)
		}

		// Check that the fingerprint has not been used
		if fpNum[1] > 0 {
			t.Errorf("Fingerprint %s for part number %d already used by %d "+
				"other parts (%d).", fp, partNum, fpNum[1], i)
		}

		// Attempt to decrypt the part
		part, err := ftCrypto.DecryptPart(st.key, encPart, padding, mac, fpNum[0])
		if err != nil {
			t.Errorf("Failed to decrypt file part number %d (%d): %+v",
				partNum, i, err)
		}

		// Make sure the decrypted part matches the original
		expectedPart, _ := st.sentParts.getPart(i % st.numParts)
		if !bytes.Equal(expectedPart, part) {
			t.Errorf("Decyrpted part number %d does not match expected (%d)."+
				"\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, part)
		}
	}
}

// Error path: tests that SentTransfer.GetEncryptedPart returns the expected
// error when no part for the given part number exists.
func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)
	prng := NewPrng(42)

	partNum := st.numParts + 1
	expectedErr := fmt.Sprintf(noPartNumErr, partNum)

	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("GetEncryptedPart did not return the expected error for a "+
			"nonexistent part number %d.\nexpected: %s\nreceived: %+v",
			partNum, expectedErr, err)
	}
}

// Error path: tests that SentTransfer.GetEncryptedPart returns the expected
// error when no fingerprints are available.
func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)
	prng := NewPrng(42)

	// Use up all the fingerprints
	for i := uint16(0); i < st.numFps; i++ {
		partNum := i % st.numParts
		_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
		if err != nil {
			t.Errorf("Error when encyrpting part number %d (%d): %+v",
				partNum, i, err)
		}
	}

	// Try to encrypt without any fingerprints
	_, _, _, _, err := st.GetEncryptedPart(5, 16, prng)
	if err != MaxRetriesErr {
		t.Errorf("GetEncryptedPart did not return MaxRetriesErr when all "+
			"fingerprints have been used.\nexpected: %s\nreceived: %+v",
			MaxRetriesErr, err)
	}
}

// Error path: tests that SentTransfer.GetEncryptedPart returns the expected
// error when encrypting the part fails due to a PRNG error.
func TestSentTransfer_GetEncryptedPart_EncryptPartError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)
	prng := NewPrngErr()

	// Create and fill fingerprint map used to check fingerprint validity
	// The first item in the uint16 slice is the fingerprint number and the
	// second item is the number of times it has been used
	fpMap := make(map[format.Fingerprint][]uint16, st.numFps)
	for num, fp := range ftCrypto.GenerateFingerprints(st.key, st.numFps) {
		fpMap[fp] = []uint16{uint16(num), 0}
	}

	partNum := uint16(0)
	expectedErr := fmt.Sprintf(encryptPartErr, partNum, "")

	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("GetEncryptedPart did not return the expected error when "+
			"the PRNG should have errored.\nexpected: %s\nreceived: %+v",
			expectedErr, err)
	}
}

// Tests that SentTransfer.SetInProgress correctly adds the part numbers for the
// given round ID to the in-progress map and sets the correct parts as
// in-progress in the state vector.
func TestSentTransfer_SetInProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedPartNums := []uint16{1, 2, 3}

	// Add parts to the in-progress list
	err, exists := st.SetInProgress(rid, expectedPartNums...)
	if err != nil {
		t.Errorf("SetInProgress returned an error: %+v", err)
	}

	// Check that the round does not already exist
	if exists {
		t.Errorf("Round %d already exists.", rid)
	}

	// Check that the round ID is in the map
	partNums, exists := st.inProgressTransfers.list[rid]
	if !exists {
		t.Errorf("Part numbers for round %d not found.", rid)
	}

	// Check that the returned part numbers are correct
	if !reflect.DeepEqual(expectedPartNums, partNums) {
		t.Errorf("Received part numbers do not match expected."+
			"\nexpected: %v\nreceived: %v", expectedPartNums, partNums)
	}

	// Check that only one item was added to the list
	if len(st.inProgressTransfers.list) > 1 {
		t.Errorf("Extra items in in-progress list."+
			"\nexpected: %d\nreceived: %d", 1, len(st.inProgressTransfers.list))
	}

	// Check that the part numbers were set on the in-progress status vector
	for i, partNum := range expectedPartNums {
		if !st.inProgressStatus.Used(uint32(partNum)) {
			t.Errorf("Part number %d not marked as used in status vector (%d).",
				partNum, i)
		}
	}

	// Check that the correct number of parts were marked as in-progress in the
	// status vector
	if int(st.inProgressStatus.GetNumUsed()) != len(expectedPartNums) {
		t.Errorf("Incorrect number of parts marked as in-progress."+
			"\nexpected: %d\nreceived: %d", len(expectedPartNums),
			st.inProgressStatus.GetNumUsed())
	}

	// Add more parts to the in-progress list
	err, exists = st.SetInProgress(rid, expectedPartNums...)
	if err != nil {
		t.Errorf("SetInProgress returned an error: %+v", err)
	}

	// Check that the round already exists
	if !exists {
		t.Errorf("Round %d should already exist.", rid)
	}

	// Check that the number of parts were marked as in-progress is unchanged
	if int(st.inProgressStatus.GetNumUsed()) != len(expectedPartNums) {
		t.Errorf("Incorrect number of parts marked as in-progress."+
			"\nexpected: %d\nreceived: %d", len(expectedPartNums),
			st.inProgressStatus.GetNumUsed())
	}
}

// Tests that SentTransfer.GetInProgress returns the correct part numbers for
// the given round ID in the in-progress map.
func TestSentTransfer_GetInProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedPartNums := []uint16{1, 2, 3, 4, 5, 6}

	// Add parts to the in-progress list
	err, _ := st.SetInProgress(rid, expectedPartNums[:3]...)
	if err != nil {
		t.Errorf("Failed to set parts %v to in-progress: %+v",
			expectedPartNums[3:], err)
	}

	// Add parts to the in-progress list
	err, _ = st.SetInProgress(rid, expectedPartNums[3:]...)
	if err != nil {
		t.Errorf("Failed to set parts %v to in-progress: %+v",
			expectedPartNums[:3], err)
	}

	// Get the in-progress parts
	receivedPartNums, exists := st.GetInProgress(rid)
	if !exists {
		t.Errorf("Failed to find parts for round %d that should exist.", rid)
	}

	// Check that the returned part numbers are correct
	if !reflect.DeepEqual(expectedPartNums, receivedPartNums) {
		t.Errorf("Received part numbers do not match expected."+
			"\nexpected: %v\nreceived: %v", expectedPartNums, receivedPartNums)
	}
}

// Tests that SentTransfer.UnsetInProgress correctly removes the part numbers
// for the given round ID from the in-progress map and unsets the correct parts
// as in-progress in the state vector.
func TestSentTransfer_UnsetInProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedPartNums := []uint16{1, 2, 3, 4, 5, 6}

	// Add parts to the in-progress list
	if err, _ := st.SetInProgress(rid, expectedPartNums[:3]...); err != nil {
		t.Errorf("Failed to set parts in-progress: %+v", err)
	}
	if err, _ := st.SetInProgress(rid, expectedPartNums[3:]...); err != nil {
		t.Errorf("Failed to set parts in-progress: %+v", err)
	}

	// Remove parts from in-progress list
	receivedPartNums, err := st.UnsetInProgress(rid)
	if err != nil {
		t.Errorf("UnsetInProgress returned an error: %+v", err)
	}

	if !reflect.DeepEqual(expectedPartNums, receivedPartNums) {
		t.Errorf("Received part numbers do not match expected."+
			"\nexpected: %v\nreceived: %v", expectedPartNums, receivedPartNums)
	}

	// Check that the round ID is not the map
	partNums, exists := st.inProgressTransfers.list[rid]
	if exists {
		t.Errorf("Part numbers for round %d found: %v", rid, partNums)
	}

	// Check that the list is empty
	if len(st.inProgressTransfers.list) != 0 {
		t.Errorf("Extra items in in-progress list."+
			"\nexpected: %d\nreceived: %d", 0, len(st.inProgressTransfers.list))
	}

	// Check that there are no set parts in the in-progress status vector
	if st.inProgressStatus.GetNumUsed() != 0 {
		t.Errorf("Failed to unset all parts in the in-progress vector."+
			"\nexpected: %d\nreceived: %d", 0, st.inProgressStatus.GetNumUsed())
	}
}

// Tests that SentTransfer.FinishTransfer removes the parts from the in-progress
// list and moved them to the finished list and that it unsets the correct parts
// in the in-progress vector in the state vector.
func TestSentTransfer_FinishTransfer(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedPartNums := []uint16{1, 2, 3}

	// Add parts to the in-progress list
	err, _ := st.SetInProgress(rid, expectedPartNums...)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress list: %+v", err)
	}

	// Move transfers to the finished list
	complete, err := st.FinishTransfer(rid)
	if err != nil {
		t.Errorf("FinishTransfer returned an error: %+v", err)
	}

	// Ensure the transfer is not reported as complete
	if complete {
		t.Error("FinishTransfer reported transfer as complete.")
	}

	// Check that the round ID is not in the in-progress map
	_, exists := st.inProgressTransfers.list[rid]
	if exists {
		t.Errorf("Found parts for round %d that should not be in map.", rid)
	}

	// Check that the round ID is in the finished map
	partNums, exists := st.finishedTransfers.list[rid]
	if !exists {
		t.Errorf("Part numbers for round %d not found.", rid)
	}

	// Check that the returned part numbers are correct
	if !reflect.DeepEqual(expectedPartNums, partNums) {
		t.Errorf("Received part numbers do not match expected."+
			"\nexpected: %+v\nreceived: %+v", expectedPartNums, partNums)
	}

	// Check that only one item was added to the list
	if len(st.finishedTransfers.list) > 1 {
		t.Errorf("Extra items in finished list."+
			"\nexpected: %d\nreceived: %d", 1, len(st.finishedTransfers.list))
	}

	// Check that there are no set parts in the in-progress status vector
	if st.inProgressStatus.GetNumUsed() != 0 {
		t.Errorf("Failed to unset all parts in the in-progress vector."+
			"\nexpected: %d\nreceived: %d", 0, st.inProgressStatus.GetNumUsed())
	}

	// Check that the part numbers were set on the finished status vector
	for i, partNum := range expectedPartNums {
		if !st.finishedStatus.Used(uint32(partNum)) {
			t.Errorf("Part number %d not marked as used in status vector (%d).",
				partNum, i)
		}
	}

	// Check that the correct number of parts were marked as finished in the
	// status vector
	if int(st.finishedStatus.GetNumUsed()) != len(expectedPartNums) {
		t.Errorf("Incorrect number of parts marked as finished."+
			"\nexpected: %d\nreceived: %d", len(expectedPartNums),
			st.finishedStatus.GetNumUsed())
	}
}

// Tests that SentTransfer.FinishTransfer returns true and sets the status to
// stopping when all file parts are marked as complete.
func TestSentTransfer_FinishTransfer_Complete(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedPartNums := make([]uint16, st.numParts)
	for i := range expectedPartNums {
		expectedPartNums[i] = uint16(i)
	}

	// Add parts to the in-progress list
	err, _ := st.SetInProgress(rid, expectedPartNums...)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress list: %+v", err)
	}

	// Move transfers to the finished list
	complete, err := st.FinishTransfer(rid)
	if err != nil {
		t.Errorf("FinishTransfer returned an error: %+v", err)
	}

	// Ensure the transfer is not reported as complete
	if !complete {
		t.Error("FinishTransfer reported transfer as not complete.")
	}

	// Test that the status is correctly set
	if st.status != Stopping {
		t.Errorf("Status not set to expected value when transfer is complete."+
			"\nexpected: %s\nreceived: %s", Stopping, st.status)
	}
}

// Error path: tests that SentTransfer.FinishTransfer returns the expected error
// when the round ID is found in the in-progress map.
func TestSentTransfer_FinishTransfer_NoRoundErr(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	rid := id.Round(5)
	expectedErr := fmt.Sprintf(noPartsForRoundErr, rid)

	// Move transfers to the finished list
	complete, err := st.FinishTransfer(rid)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("Did not get expected error when round ID not in in-progress "+
			"map.\nexpected: %s\nreceived: %+v", expectedErr, err)
	}

	// Ensure the transfer is not reported as complete
	if complete {
		t.Error("FinishTransfer reported transfer as complete.")
	}
}

// Tests that SentTransfer.GetUnsentPartNums returns only part numbers that are
// not marked as in-progress or finished.
func TestSentTransfer_GetUnsentPartNums(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(18, 27, kv, t)

	expectedPartNums := make([]uint16, 0, st.numParts/3)

	// Loop through each part and set it individually
	for i := uint16(0); i < st.numParts; i++ {
		switch i % 3 {
		case 0:
			// Part is sent (in-progress)
			_, _ = st.SetInProgress(id.Round(i), i)
		case 1:
			// Part is sent and arrived (finished)
			_, _ = st.SetInProgress(id.Round(i), i)
			_, _ = st.FinishTransfer(id.Round(i))
		case 2:
			// Part is unsent (neither in-progress nor arrived)
			expectedPartNums = append(expectedPartNums, i)
		}
	}

	unsentPartNums := st.GetUnsentPartNums()
	if !reflect.DeepEqual(expectedPartNums, unsentPartNums) {
		t.Errorf("Unexpected unsent part numbers.\nexpected: %d\nreceived: %d",
			expectedPartNums, unsentPartNums)
	}
}

// Tests that SentTransfer.GetSentRounds returns the expected round IDs when
// every round is either in-progress, finished, or unsent.
func TestSentTransfer_GetSentRounds(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(18, 27, kv, t)

	expectedRounds := make([]id.Round, 0, st.numParts/3)

	// Loop through each part and set it individually
	for i := uint16(0); i < st.numParts; i++ {
		rid := id.Round(i)
		switch i % 3 {
		case 0:
			// Part is sent (in-progress)
			_, _ = st.SetInProgress(rid, i)
			expectedRounds = append(expectedRounds, rid)
		case 1:
			// Part is sent and arrived (finished)
			_, _ = st.SetInProgress(rid, i)
			_, _ = st.FinishTransfer(rid)
		case 2:
			// Part is unsent (neither in-progress nor arrived)
		}
	}

	// Get the sent
	sentRounds := st.GetSentRounds()
	sort.SliceStable(sentRounds,
		func(i, j int) bool { return sentRounds[i] < sentRounds[j] })

	if !reflect.DeepEqual(expectedRounds, sentRounds) {
		t.Errorf("Unexpected sent rounds.\nexpected: %d\nreceived: %d",
			expectedRounds, sentRounds)
	}
}

////////////////////////////////////////////////////////////////////////////////
// Storage Function Testing                                                   //
////////////////////////////////////////////////////////////////////////////////

// Tests that loadSentTransfer returns a SentTransfer that matches the original
// object in memory.
func Test_loadSentTransfer(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, expectedST := newRandomSentTransfer(16, 24, kv, t)
	err, _ := expectedST.SetInProgress(5, 3, 4, 5)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress transfer: %+v", err)
	}
	err, _ = expectedST.SetInProgress(10, 10, 11, 12)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress transfer: %+v", err)
	}

	_, err = expectedST.FinishTransfer(10)
	if err != nil {
		t.Errorf("Failed to move parts to finished transfer: %+v", err)
	}

	loadedST, err := loadSentTransfer(tid, kv)
	if err != nil {
		t.Errorf("loadSentTransfer returned an error: %+v", err)
	}

	// Progress callbacks cannot be compared
	loadedST.progressCallbacks = expectedST.progressCallbacks

	if !reflect.DeepEqual(expectedST, loadedST) {
		t.Errorf("Loaded SentTransfer does not match expected."+
			"\nexpected: %+v\nreceived: %+v", expectedST, loadedST)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when no
// transfer with the given ID exists in storage.
func Test_loadSentTransfer_LoadInfoError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid := ftCrypto.UnmarshalTransferID([]byte("invalidTransferID"))

	expectedErr := strings.Split(loadSentStoreErr, "%")[0]
	_, err := loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when no "+
			"transfer with the ID %s exists in storage."+
			"\nexpected: %s\nreceived: %+v", tid, expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// fingerprint state vector was deleted from storage.
func Test_loadSentTransfer_LoadFingerprintStateVectorError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the fingerprint state vector from storage
	err := st.fpVector.Delete()
	if err != nil {
		t.Errorf("Failed to delete the fingerprint vector: %+v", err)
	}

	expectedErr := strings.Split(loadSentFpVectorErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the fingerprint vector was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// part store was deleted from storage.
func Test_loadSentTransfer_LoadPartStoreError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the part store from storage
	err := st.sentParts.delete()
	if err != nil {
		t.Errorf("Failed to delete the part store: %+v", err)
	}

	expectedErr := strings.Split(loadSentPartStoreErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the part store was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// in-progress transfers bundle was deleted from storage.
func Test_loadSentTransfer_LoadInProgressTransfersError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the in-progress transfers bundle from storage
	err := st.inProgressTransfers.delete()
	if err != nil {
		t.Errorf("Failed to delete the in-progress transfers bundle: %+v", err)
	}

	expectedErr := strings.Split(loadInProgressTransfersErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the in-progress transfers bundle was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// finished transfer bundle was deleted from storage.
func Test_loadSentTransfer_LoadFinishedTransfersError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the finished transfers bundle from storage
	err := st.finishedTransfers.delete()
	if err != nil {
		t.Errorf("Failed to delete the finished transfers bundle: %+v", err)
	}

	expectedErr := strings.Split(loadFinishedTransfersErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the finished transfers bundle was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// in-progress status state vector was deleted from storage.
func Test_loadSentTransfer_LoadInProgressStateVectorError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the in-progress state vector from storage
	err := st.inProgressStatus.Delete()
	if err != nil {
		t.Errorf("Failed to delete the in-progress vector: %+v", err)
	}

	expectedErr := strings.Split(loadSentInProgressVectorErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the in-progress vector was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadSentTransfer returns the expected error when the
// finished status state vector was deleted from storage.
func Test_loadSentTransfer_LoadFinishedStateVectorError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, st := newRandomSentTransfer(16, 24, kv, t)

	// Delete the finished state vector from storage
	err := st.finishedStatus.Delete()
	if err != nil {
		t.Errorf("Failed to delete the finished vector: %+v", err)
	}

	expectedErr := strings.Split(loadSentFinishedVectorErr, "%")[0]
	_, err = loadSentTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadSentTransfer did not return the expected error when "+
			"the finished vector was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that SentTransfer.saveInfo saves the expected data to storage.
func TestSentTransfer_saveInfo(t *testing.T) {
	st := &SentTransfer{
		key:      ftCrypto.UnmarshalTransferKey([]byte("key")),
		numParts: 16,
		kv:       versioned.NewKV(make(ekv.Memstore)),
	}

	err := st.saveInfo()
	if err != nil {
		t.Errorf("saveInfo returned an error: %+v", err)
	}

	vo, err := st.kv.Get(sentTransferKey, sentTransferVersion)
	if err != nil {
		t.Errorf("Failed to load SentTransfer from storage: %+v", err)
	}

	if !bytes.Equal(st.marshal(), vo.Data) {
		t.Errorf("Marshalled data loaded from storage does not match expected."+
			"\nexpected: %+v\nreceived: %+v", st.marshal(), vo.Data)
	}
}

// Tests that SentTransfer.loadInfo loads a saved SentTransfer from storage.
func TestSentTransfer_loadInfo(t *testing.T) {
	st := &SentTransfer{
		recipient: id.NewIdFromString("recipient", id.User, t),
		key:       ftCrypto.UnmarshalTransferKey([]byte("key")),
		numParts:  16,
		kv:        versioned.NewKV(make(ekv.Memstore)),
	}

	err := st.saveInfo()
	if err != nil {
		t.Errorf("failed to save new SentTransfer to storage: %+v", err)
	}

	loadedST := &SentTransfer{kv: st.kv}
	err = loadedST.loadInfo()
	if err != nil {
		t.Errorf("load returned an error: %+v", err)
	}

	if !reflect.DeepEqual(st, loadedST) {
		t.Errorf("Loaded SentTransfer does not match expected."+
			"\nexpected: %+v\nreceived: %+v", st, loadedST)
	}
}

// Error path: tests that SentTransfer.loadInfo returns an error when there is
// no object in storage to load
func TestSentTransfer_loadInfo_Error(t *testing.T) {
	loadedST := &SentTransfer{kv: versioned.NewKV(make(ekv.Memstore))}
	err := loadedST.loadInfo()
	if err == nil {
		t.Errorf("Loaded object that should not be in storage: %+v", err)
	}
}

// Tests that SentTransfer.delete removes all data from storage.
func TestSentTransfer_delete(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, st := newRandomSentTransfer(16, 24, kv, t)

	// Add in-progress transfers
	err, _ := st.SetInProgress(5, 3, 4, 5)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress transfer: %+v", err)
	}
	err, _ = st.SetInProgress(10, 10, 11, 12)
	if err != nil {
		t.Errorf("Failed to add parts to in-progress transfer: %+v", err)
	}

	// Mark last in-progress transfer and finished
	_, err = st.FinishTransfer(10)
	if err != nil {
		t.Errorf("Failed to move parts to finished transfer: %+v", err)
	}

	// Delete everything from storage
	err = st.delete()
	if err != nil {
		t.Errorf("delete returned an error: %+v", err)
	}

	// Check that the SentTransfer info was deleted
	err = st.loadInfo()
	if err == nil {
		t.Error("Successfully loaded SentTransfer info from storage when it " +
			"should have been deleted.")
	}

	// Check that the parts store were deleted
	_, err = loadPartStore(st.kv)
	if err == nil {
		t.Error("Successfully loaded file parts from storage when it should " +
			"have been deleted.")
	}

	// Check that the in-progress transfers were deleted
	_, err = loadTransferredBundle(inProgressKey, st.kv)
	if err == nil {
		t.Error("Successfully loaded in-progress transfers from storage when " +
			"it should have been deleted.")
	}

	// Check that the finished transfers were deleted
	_, err = loadTransferredBundle(finishedKey, st.kv)
	if err == nil {
		t.Error("Successfully loaded finished transfers from storage when " +
			"it should have been deleted.")
	}

	// Check that the fingerprint vector was deleted
	_, err = utility.LoadStateVector(st.kv, sentFpVectorKey)
	if err == nil {
		t.Error("Successfully loaded fingerprint vector from storage when it " +
			"should have been deleted.")
	}

	// Check that the in-progress status vector was deleted
	_, err = utility.LoadStateVector(st.kv, sentInProgressVectorKey)
	if err == nil {
		t.Error("Successfully loaded in-progress vector from storage when it " +
			"should have been deleted.")
	}

	// Check that the finished status vector was deleted
	_, err = utility.LoadStateVector(st.kv, sentFinishedVectorKey)
	if err == nil {
		t.Error("Successfully loaded finished vector from storage when it " +
			"should have been deleted.")
	}

}

// Tests that SentTransfer.deleteInfo removes the saved SentTransfer data from
// storage.
func TestSentTransfer_deleteInfo(t *testing.T) {
	st := &SentTransfer{
		key:      ftCrypto.UnmarshalTransferKey([]byte("key")),
		numParts: 16,
		kv:       versioned.NewKV(make(ekv.Memstore)),
	}

	// Save from storage
	err := st.saveInfo()
	if err != nil {
		t.Errorf("failed to save new SentTransfer to storage: %+v", err)
	}

	// Delete from storage
	err = st.deleteInfo()
	if err != nil {
		t.Errorf("deleteInfo returned an error: %+v", err)
	}

	// Make sure deleted object cannot be loaded from storage
	_, err = st.kv.Get(sentTransferKey, sentTransferVersion)
	if err == nil {
		t.Error("Loaded object that should be deleted from storage.")
	}
}

// Tests that a SentTransfer marshalled with SentTransfer.marshal and then
// unmarshalled with unmarshalSentTransfer matches the original.
func TestSentTransfer_marshal_unmarshalSentTransfer(t *testing.T) {
	st := &SentTransfer{
		recipient: id.NewIdFromString("testRecipient", id.User, t),
		key:       ftCrypto.UnmarshalTransferKey([]byte("key")),
		numParts:  16,
		numFps:    20,
		status:    Stopped,
	}

	marshaledData := st.marshal()

	recipient, key, numParts, numFps, status := unmarshalSentTransfer(marshaledData)

	if !st.recipient.Cmp(recipient) {
		t.Errorf("Failed to get recipient ID.\nexpected: %s\nreceived: %s",
			st.recipient, recipient)
	}

	if st.key != key {
		t.Errorf("Failed to get expected key.\nexpected: %s\nreceived: %s",
			st.key, key)
	}

	if st.numParts != numParts {
		t.Errorf("Failed to get expected number of parts."+
			"\nexpected: %d\nreceived: %d", st.numParts, numParts)
	}

	if st.numFps != numFps {
		t.Errorf("Failed to get expected number of fingerprints."+
			"\nexpected: %d\nreceived: %d", st.numFps, numFps)
	}

	if st.status != status {
		t.Errorf("Failed to get expected transfer status."+
			"\nexpected: %s\nreceived: %s", st.status, status)
	}
}

// Consistency test: tests that makeSentTransferPrefix returns the expected
// prefixes for the provided transfer IDs.
func Test_makeSentTransferPrefix_Consistency(t *testing.T) {
	prng := NewPrng(42)
	expectedPrefixes := []string{
		"FileTransferSentTransferStoreU4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVI=",
		"FileTransferSentTransferStore39ebTXZCm2F6DJ+fDTulWwzA1hRMiIU1hBrL4HCbB1g=",
		"FileTransferSentTransferStoreCD9h03W8ArQd9PkZKeGP2p5vguVOdI6B555LvW/jTNw=",
		"FileTransferSentTransferStoreuoQ+6NY+jE/+HOvqVG2PrBPdGqwEzi6ih3xVec+ix44=",
		"FileTransferSentTransferStoreGwuvrogbgqdREIpC7TyQPKpDRlp4YgYWl4rtDOPGxPM=",
		"FileTransferSentTransferStorernvD4ElbVxL+/b4MECiH4QDazS2IX2kstgfaAKEcHHA=",
		"FileTransferSentTransferStoreceeWotwtwlpbdLLhKXBeJz8FySMmgo4rBW44F2WOEGE=",
		"FileTransferSentTransferStoreSYlH/fNEQQ7UwRYCP6jjV2tv7Sf/iXS6wMr9mtBWkrE=",
		"FileTransferSentTransferStoreNhnnOJZN/ceejVNDc2Yc/WbXT+weG4lJGrcjbkt1IWI=",
		"FileTransferSentTransferStorekM8r60LDyicyhWDxqsBnzqbov0bUqytGgEAsX7KCDog=",
	}

	for i, expected := range expectedPrefixes {
		tid, _ := ftCrypto.NewTransferID(prng)
		prefix := makeSentTransferPrefix(tid)

		if expected != prefix {
			t.Errorf("New SentTransfer prefix does not match expected (%d)."+
				"\nexpected: %s\nreceived: %s", i, expected, prefix)
		}
	}
}

// Tests that each of the elements in the uint32 slice returned by
// uint16SliceToUint32Slice matches the elements in the original uint16 slice.
func Test_uint16SliceToUint32Slice(t *testing.T) {
	prng := rand.New(rand.NewSource(42))
	uint16Slice := make([]uint16, 100)

	for i := range uint16Slice {
		uint16Slice[i] = uint16(prng.Uint32())
	}

	uint32Slice := uint16SliceToUint32Slice(uint16Slice)

	// Check that each element is correct
	for i, expected := range uint16Slice {
		if uint32(expected) != uint32Slice[i] {
			t.Errorf("Element #%d is incorrect.\nexpected: %d\nreceived: %d",
				i, uint32(expected), uint32Slice[i])
		}
	}
}

// newRandomSentTransfer generates a new SentTransfer with random data.
func newRandomSentTransfer(numParts, numFps uint16, kv *versioned.KV,
	t *testing.T) (ftCrypto.TransferID, *SentTransfer) {
	// Generate new PRNG with the seed generated by multiplying the pointer for
	// numParts with the current UNIX time in nanoseconds
	seed, _ := strconv.ParseInt(fmt.Sprintf("%d", &numParts), 10, 64)
	seed *= netTime.Now().UnixNano()
	prng := NewPrng(seed)

	recipient, _ := id.NewRandomID(prng, id.User)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	parts := make([][]byte, numParts)
	for i := uint16(0); i < numParts; i++ {
		parts[i] = make([]byte, 16)
		_, err := prng.Read(parts[i])
		if err != nil {
			t.Errorf("Failed to generate random part: %+v", err)
		}
	}

	st, err := NewSentTransfer(recipient, tid, key, parts, numFps, nil, 0, kv)
	if err != nil {
		t.Errorf("Failed to create new SentTansfer: %+v", err)
	}

	return tid, st
}

// checkSentProgress compares the output of SentTransfer.GetProgress to expected
// values.
func checkSentProgress(completed bool, sent, arrived, total uint16,
	eCompleted bool, eSent, eArrived, eTotal uint16) error {
	if eCompleted != completed || eSent != sent || eArrived != arrived ||
		eTotal != total {
		return errors.Errorf("Returned progress does not match expected."+
			"\n          completed  sent  arrived  total"+
			"\nexpected:     %5t   %3d      %3d    %3d"+
			"\nreceived:     %5t   %3d      %3d    %3d",
			eCompleted, eSent, eArrived, eTotal,
			completed, sent, arrived, total)
	}

	return nil
}

// checkSentTracker checks that the SentPartTracker is reporting the correct
// values for each part. Also checks that SentPartTracker.GetNumParts returns
// the expected value (make sure numParts comes from a correct source).
func checkSentTracker(track SentPartTracker, numParts uint16, inProgress,
	finished []uint16, t *testing.T) {
	if track.GetNumParts() != numParts {
		t.Errorf("Tracker reported incorrect number of parts."+
			"\nexpected: %d\nreceived: %d", numParts, track.GetNumParts())
		return
	}

	for partNum := uint16(0); partNum < numParts; partNum++ {
		var done bool
		for _, inProgressNum := range inProgress {
			if inProgressNum == partNum {
				if track.GetPartStatus(partNum) != interfaces.FpSent {
					t.Errorf("Part number %d has unexpected status."+
						"\nexpected: %d\nreceived: %d", partNum,
						interfaces.FpSent, track.GetPartStatus(partNum))
				}
				done = true
				break
			}
		}
		if done {
			continue
		}

		for _, finishedNum := range finished {
			if finishedNum == partNum {
				if track.GetPartStatus(partNum) != interfaces.FpArrived {
					t.Errorf("Part number %d has unexpected status."+
						"\nexpected: %d\nreceived: %d", partNum,
						interfaces.FpArrived, track.GetPartStatus(partNum))
				}
				done = true
				break
			}
		}
		if done {
			continue
		}

		if track.GetPartStatus(partNum) != interfaces.FpUnsent {
			t.Errorf("Part number %d has incorrect status."+
				"\nexpected: %d\nreceived: %d",
				partNum, interfaces.FpUnsent, track.GetPartStatus(partNum))
		}
	}
}